summaryrefslogtreecommitdiff
path: root/common/parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'common/parser.cpp')
-rw-r--r--common/parser.cpp538
1 files changed, 538 insertions, 0 deletions
diff --git a/common/parser.cpp b/common/parser.cpp
new file mode 100644
index 0000000..8c2533a
--- /dev/null
+++ b/common/parser.cpp
@@ -0,0 +1,538 @@
+/*
+ * parser.cpp
+ *
+ * Created on: 16.09.2012
+ * Author: savop
+ */
+
+// uncomment this to enable debuging of the grammar
+//#define BOOST_SPIRIT_DEBUG
+
+#include <string>
+#include "../include/parser.h"
+#include "../include/plugin.h"
+#include <boost/spirit/include/classic.hpp>
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+
+using namespace std;
+using namespace boost;
+
+namespace sp = boost::spirit::classic;
+
+namespace upnp {
+
+
+// This is the standard callback function which will be overloaded
+// with all the specific callbacks
+typedef function2<void, const char*, const char*> expCallback;
+typedef function1<void, const char*> propCallback;
+typedef function1<void, const char*> opCallback;
+typedef function1<void, const char> charCallback;
+typedef function1<void, int> intCallback;
+
+// The defined ColumnNames
+/** @private */
+struct cProperties : sp::symbols<const char*> {
+ //struct cProperties : symbols<> {
+ cProperties(){
+ add
+ (property::object::KEY_TITLE, "title")
+ (property::object::KEY_CREATOR, "creator")
+ (property::object::KEY_DESCRIPTION, "description")
+ (property::object::KEY_LONG_DESCRIPTION, "longDescription")
+ (property::object::KEY_CLASS, "class")
+ (property::object::KEY_DATE, "date")
+ (property::object::KEY_LANGUAGE, "language")
+ (property::resource::KEY_PROTOCOL_INFO, "protocolInfo")
+ ;
+ }
+} Properties;
+
+/** @private */
+struct cOperators : sp::symbols<const char*> {
+ cOperators(){
+ add
+ ("=", "==")
+ ("!=", "!=")
+ ("<", "<")
+ (">", ">")
+ ("<=", "<=")
+ (">=", ">=")
+ ("contains", "LIKE")
+ ("doesNotContain", "NOT LIKE")
+ ("derivedfrom", "LIKE")
+ ;
+ }
+} Operators;
+
+/** @private */
+struct cConcatOperators : sp::symbols<const char*> {
+ cConcatOperators(){
+ add
+ ("and", "AND")
+ ("or", "OR")
+ ;
+ }
+} ConcatOperators;
+
+/** @private */
+struct cExistanceOperator : sp::symbols<const char*> {
+ cExistanceOperator(){
+ add
+ ("true", "NOT NULL")
+ ("false", "NULL")
+ ;
+ }
+} Existance;
+
+// THE GRAMMAR!
+// This is the grammar including the functors which calls the member functions
+// of search. The callback definitions at the end of the constructor are
+// essential. DO NOT MODIFY if you don't know how!
+/** @private */
+struct cSearchGrammar : public sp::grammar<cSearchGrammar> {
+ // The callbacks members
+ charCallback &endBrackedExp;
+ expCallback &pushSimpleExp;
+ opCallback &pushOperator;
+ expCallback &pushQuotedValue;
+ opCallback &pushExistance;
+ propCallback &pushProperty;
+ opCallback &pushConcatOp;
+ charCallback &startBrackedExp;
+
+ // Constructor with the callback functions
+ cSearchGrammar(
+ charCallback &endBrackedExp,
+ expCallback &pushSimpleExp,
+ opCallback &pushOperator,
+ expCallback &pushQuotedValue,
+ opCallback &pushExistance,
+ propCallback &pushProperty,
+ opCallback &pushConcatOp,
+ charCallback &startBrackedExp):
+ endBrackedExp(endBrackedExp),
+ pushSimpleExp(pushSimpleExp),
+ pushOperator(pushOperator),
+ pushQuotedValue(pushQuotedValue),
+ pushExistance(pushExistance),
+ pushProperty(pushProperty),
+ pushConcatOp(pushConcatOp),
+ startBrackedExp(startBrackedExp){}
+
+ template <typename scanner>
+ /** @private */
+ struct definition {
+ sp::rule<scanner> searchCrit, searchExp, logOp, \
+ relExp, binOp, relOp, stringOp, \
+ existsOp, boolVal, quotedVal, \
+ wChar, property, brackedExp, exp;
+ const sp::rule<scanner> &start(){
+ return searchCrit;
+ }
+ definition(const cSearchGrammar &self){
+ /*************************************************************************\
+ * *
+ * The grammar of a UPnP search expression *
+ * *
+ * searchCrit ::= searchExp | asterisk *
+ * *
+ * searchExp ::= relExp | *
+ * searchExp wChar+ logOp wChar+ searchExp | *
+ * '(' wChar* searchExp wChar* ')' *
+ * *
+ * logOp ::= 'and' | 'or' *
+ * *
+ * relExp ::= property wChar+ binOp wChar+ quotedVal | *
+ * property wChar* existsOp wChar+ boolVal *
+ * *
+ * binOp ::= relOp | stringOp *
+ * *
+ * relOp ::= '=' | '!=' | '<' | '<=' | '>' | '>=' *
+ * *
+ * stringOp ::= 'contains' | 'doesNotContain' | 'derivedfrom' *
+ * *
+ * existsOp ::= 'exists' *
+ * *
+ * boolVal ::= 'true' | 'false' *
+ * *
+ * quotedVal ::= dQuote escapedQuote dQuote *
+ * *
+ * wChar ::= space | hTab | lineFeed | *
+ * vTab | formFeed | return *
+ * *
+ * property ::= See ContentDirectory Section 2.4 *
+ * *
+ * escapedQuote ::= See ContentDirectory Section 2.3.1 *
+ * *
+ \*************************************************************************/
+ searchCrit = searchExp | "*";
+
+ searchExp = exp >> *(+wChar >> logOp >> +wChar >> exp);
+ ;
+
+ exp = relExp
+ | brackedExp
+ ;
+
+ brackedExp = sp::confix_p(
+ sp::ch_p('(')[self.startBrackedExp],
+ *wChar >> searchExp >> *wChar,
+ sp::ch_p(')')[self.endBrackedExp]
+ )
+ ;
+
+ logOp = ConcatOperators[self.pushConcatOp]
+ ;
+
+ relExp = (property >> +wChar >> binOp >> +wChar >> quotedVal) [self.pushSimpleExp]
+ | (property >> +wChar >> existsOp >> +wChar >> boolVal) [self.pushSimpleExp]
+ ;
+
+ binOp = Operators[self.pushOperator]
+ ;
+
+ existsOp = sp::str_p("exists")
+ ;
+
+ boolVal = Existance[self.pushExistance]
+ ;
+
+ quotedVal = sp::confix_p('"', (*sp::c_escape_ch_p)[self.pushQuotedValue], '"')
+ ;
+
+ wChar = sp::space_p
+ ;
+
+ property = Properties[self.pushProperty]
+ ;
+
+ // Debug mode
+#ifdef BOOST_SPIRIT_DEBUG
+ BOOST_SPIRIT_DEBUG_NODE(searchCrit);
+ BOOST_SPIRIT_DEBUG_NODE(searchExp);
+ BOOST_SPIRIT_DEBUG_NODE(logOp);
+ BOOST_SPIRIT_DEBUG_NODE(relExp);
+ BOOST_SPIRIT_DEBUG_NODE(binOp);
+ BOOST_SPIRIT_DEBUG_NODE(relOp);
+ BOOST_SPIRIT_DEBUG_NODE(stringOp);
+ BOOST_SPIRIT_DEBUG_NODE(existsOp);
+ BOOST_SPIRIT_DEBUG_NODE(boolVal);
+ BOOST_SPIRIT_DEBUG_NODE(quotedVal);
+ BOOST_SPIRIT_DEBUG_NODE(wChar);
+ BOOST_SPIRIT_DEBUG_NODE(property);
+#endif
+ }
+ };
+};
+
+/**********************************************\
+ * *
+ * The actors *
+ * *
+ \**********************************************/
+
+void cSearch::pushEndBrackedExp(const char){
+ sqlWhereStmt << ") ";
+}
+
+void cSearch::pushExpression(const char*, const char*){
+
+ sqlWhereStmt << currentProperty << " " << currentOperator << " " << currentValue << " ";
+
+ return;
+}
+
+void cSearch::pushProperty(string property){
+ currentProperty = property;
+}
+
+void cSearch::pushOperator(string op){
+ currentOperator = op;
+}
+
+void cSearch::pushValue(const char* start, const char* end){
+ stringstream s;
+
+ s << "'";
+
+ if(currentOperator.find("LIKE") != string::npos)
+ s << "%" << string(start, end) << "%";
+ else
+ s << string(start, end);
+
+ s << "'";
+
+ currentValue = s.str();
+}
+
+void cSearch::pushExistance(string Exists){
+ currentValue = Exists;
+ currentOperator = "IS";
+}
+
+void cSearch::pushConcatOp(string Operator){
+ sqlWhereStmt << Operator << " ";
+}
+
+void cSearch::pushStartBrackedExp(const char){
+ sqlWhereStmt << "( ";
+}
+
+/**********************************************\
+ * *
+ * The rest *
+ * *
+ \**********************************************/
+
+string cSearch::parse(const string& search){
+ static cSearch searchCrit;
+
+ if(searchCrit.parseCriteria(search)){
+ return searchCrit.sqlWhereStmt.str();
+ }
+ return string();
+}
+
+bool cSearch::parseCriteria(string search){
+
+ sqlWhereStmt.str(string());
+
+ charCallback endBrackedExpCB(bind(&cSearch::pushEndBrackedExp, this, _1));
+ expCallback pushSimpleExpCB(bind(&cSearch::pushExpression, this, _1, _2));
+ opCallback pushOperatorCB(bind(&cSearch::pushOperator, this, _1));
+ expCallback pushQuotedValueCB(bind(&cSearch::pushValue, this, _1, _2));
+ opCallback pushExistanceCB(bind(&cSearch::pushExistance, this, _1));
+ propCallback pushPropertyCB(bind(&cSearch::pushProperty, this, _1));
+ opCallback pushConcatOpCB(bind(&cSearch::pushConcatOp, this, _1));
+ charCallback startBrackedExpCB(bind(&cSearch::pushStartBrackedExp, this, _1));
+
+ // Craft the grammar
+ cSearchGrammar Grammar(endBrackedExpCB,
+ pushSimpleExpCB,
+ pushOperatorCB,
+ pushQuotedValueCB,
+ pushExistanceCB,
+ pushPropertyCB,
+ pushConcatOpCB,
+ startBrackedExpCB);
+
+ if(!sp::parse(search.c_str(), Grammar).full){
+ return false;
+ }
+ return true;
+}
+
+cSearch::cSearch(){}
+
+cSearch::~cSearch(){}
+
+/**********************************************\
+ * *
+ * The filter *
+ * *
+ \**********************************************/
+
+/** @private */
+struct cFilterGrammar : public sp::grammar<cFilterGrammar> {
+ // The callback members
+ expCallback &pushProperty;
+ charCallback &pushAsterisk;
+
+ cFilterGrammar(
+ expCallback &pushProperty,
+ charCallback &pushAsterisk):
+ pushProperty(pushProperty),
+ pushAsterisk(pushAsterisk){}
+
+ template <typename scanner>
+ /** @private */
+ struct definition {
+ sp::rule<scanner> filterCrit, filterExp, property, propString;
+
+ const sp::rule<scanner> &start(){
+ return filterCrit;
+ }
+ definition(const cFilterGrammar &self){
+ filterCrit = sp::ch_p('*')[self.pushAsterisk]
+ | filterExp
+ ;
+
+ filterExp = property >> *(sp::ch_p(',') >> *sp::space_p >> filterExp)
+ ;
+
+ property = propString[self.pushProperty]
+ ;
+
+ // TODO: improve this grammer.
+ // A property has the following pattern:
+ // [<namespace>:]property[@attribute] OR @id, @parentID, @restricted
+ //
+ propString = *(sp::alpha_p | sp::ch_p(':') | sp::ch_p('@'))
+ ;
+ }
+ };
+};
+
+cFilterCriteria::cFilterCriteria(){}
+
+cFilterCriteria::~cFilterCriteria(){}
+
+StringList cFilterCriteria::parse(const string& filter){
+ static cFilterCriteria filterParser;
+
+ StringList list;
+
+ if(filterParser.parseFilter(filter)){
+ list = filterParser.getFilterList();
+ }
+
+ return list;
+}
+
+bool cFilterCriteria::parseFilter(string filter){
+
+ mFilterList.clear();
+
+ mFilterList.push_back(property::object::KEY_OBJECTID);
+ mFilterList.push_back(property::object::KEY_PARENTID);
+ mFilterList.push_back(property::object::KEY_TITLE);
+ mFilterList.push_back(property::object::KEY_CLASS);
+ mFilterList.push_back(property::object::KEY_RESTRICTED);
+ mFilterList.push_back(property::resource::KEY_RESOURCE);
+ mFilterList.push_back(property::resource::KEY_PROTOCOL_INFO);
+
+ if(filter.empty()){
+ return true;
+ }
+
+ charCallback pushAsteriskCB(bind(&cFilterCriteria::pushAsterisk,this,_1));
+ expCallback pushPropertyCB(bind(&cFilterCriteria::pushProperty,this,_1, _2));
+
+ cFilterGrammar Grammar(pushPropertyCB, pushAsteriskCB);
+
+ if(!sp::parse(filter.c_str(), Grammar).full){
+ return false;
+ }
+ return true;
+}
+
+/**********************************************\
+ * *
+ * The actors *
+ * *
+ \**********************************************/
+
+void cFilterCriteria::pushProperty(const char* start, const char* end){
+ string s(start, end);
+
+ for(StringList::iterator it = mFilterList.begin(); it != mFilterList.end(); ++it){
+ if((*it).compare(s) == 0)
+ return;
+ }
+
+ mFilterList.push_back(s);
+}
+
+void cFilterCriteria::pushAsterisk(const char){
+ mFilterList.clear();
+ mFilterList.push_back("*");
+ return;
+}
+
+/**********************************************\
+ * *
+ * The sorter *
+ * *
+ \**********************************************/
+
+/** @private */
+struct cSortGrammar : public sp::grammar<cSortGrammar> {
+ // The callback members
+ propCallback &pushProperty;
+ charCallback &pushDirection;
+
+ cSortGrammar(
+ propCallback &pushProperty,
+ charCallback &pushDirection):
+ pushProperty(pushProperty),
+ pushDirection(pushDirection){}
+
+ template <typename scanner>
+ /** @private */
+ struct definition {
+ sp::rule<scanner> sortCrit, sortExp, property, direction;
+
+ const sp::rule<scanner> &start(){
+ return sortCrit;
+ }
+ definition(const cSortGrammar &self){
+ sortCrit = sortExp
+ ;
+
+ sortExp = direction >> property >> *(sp::ch_p(',') >> sortExp)
+ ;
+
+ direction = sp::sign_p[self.pushDirection]
+ ;
+
+ property = Properties[self.pushProperty]
+ ;
+ }
+ };
+};
+
+cSortCriteria::cSortCriteria(){}
+
+cSortCriteria::~cSortCriteria(){}
+
+list<SortCrit> cSortCriteria::parse(const string& sort){
+ static cSortCriteria sortParser;
+
+ list<SortCrit> list;
+
+ if(sortParser.parseSort(sort)){
+ list = sortParser.getSortList();
+ }
+
+ return list;
+}
+
+bool cSortCriteria::parseSort(string sort){
+
+ mCriteriaList.clear();
+
+ if(sort.empty()){
+ return true;
+ }
+
+ charCallback pushDirectionCB(bind(&cSortCriteria::pushDirection,this,_1));
+ propCallback pushPropertyCB(bind(&cSortCriteria::pushProperty,this,_1));
+
+ cSortGrammar Grammar(pushPropertyCB, pushDirectionCB);
+
+ if(!sp::parse(sort.c_str(), Grammar).full){
+ return false;
+ }
+ return true;
+}
+
+/**********************************************\
+ * *
+ * The actors *
+ * *
+ \**********************************************/
+
+void cSortCriteria::pushProperty(string property){
+ mCurrentCrit.property = property;
+ mCriteriaList.push_back(mCurrentCrit);
+ return;
+}
+
+void cSortCriteria::pushDirection(bool desc){
+ mCurrentCrit.sortDescending = (desc)?true:false;
+ return;
+}
+
+} // namespace upnp