aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--XBMC.xcodeproj/project.pbxproj10
-rw-r--r--project/VS2010Express/XBMC.vcxproj2
-rw-r--r--project/VS2010Express/XBMC.vcxproj.filters8
-rw-r--r--xbmc/dbwrappers/DatabaseQuery.cpp529
-rw-r--r--xbmc/dbwrappers/DatabaseQuery.h144
-rw-r--r--xbmc/dbwrappers/Makefile1
-rw-r--r--xbmc/playlists/SmartPlayList.cpp491
-rw-r--r--xbmc/playlists/SmartPlayList.h113
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 &param, 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 &param,
+ 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 &param,
+ 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 &parameter) 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 &param, 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 &param, 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 &param,
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 &param,
- 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 &param,
- 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 &parameter) 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: