forked from teamnwah/openmw-tes3coop
Merge branch 'filter'
commit
f605dcdd24
@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
#include "andnode.hpp"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
CSMFilter::AndNode::AndNode (const std::vector<boost::shared_ptr<Node> >& nodes)
|
||||||
|
: NAryNode (nodes, "and")
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool CSMFilter::AndNode::test (const CSMWorld::IdTable& table, int row,
|
||||||
|
const std::map<int, int>& columns) const
|
||||||
|
{
|
||||||
|
int size = getSize();
|
||||||
|
|
||||||
|
for (int i=0; i<size; ++i)
|
||||||
|
if (!(*this)[i].test (table, row, columns))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
#ifndef CSM_FILTER_ANDNODE_H
|
||||||
|
#define CSM_FILTER_ANDNODE_H
|
||||||
|
|
||||||
|
#include "narynode.hpp"
|
||||||
|
|
||||||
|
namespace CSMFilter
|
||||||
|
{
|
||||||
|
class AndNode : public NAryNode
|
||||||
|
{
|
||||||
|
bool mAnd;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
AndNode (const std::vector<boost::shared_ptr<Node> >& nodes);
|
||||||
|
|
||||||
|
virtual bool test (const CSMWorld::IdTable& table, int row,
|
||||||
|
const std::map<int, int>& columns) const;
|
||||||
|
///< \return Can the specified table row pass through to filter?
|
||||||
|
/// \param columns column ID to column index mapping
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
#include "booleannode.hpp"
|
||||||
|
|
||||||
|
CSMFilter::BooleanNode::BooleanNode (bool true_) : mTrue (true_) {}
|
||||||
|
|
||||||
|
bool CSMFilter::BooleanNode::test (const CSMWorld::IdTable& table, int row,
|
||||||
|
const std::map<int, int>& columns) const
|
||||||
|
{
|
||||||
|
return mTrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CSMFilter::BooleanNode::toString (bool numericColumns) const
|
||||||
|
{
|
||||||
|
return mTrue ? "true" : "false";
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
#ifndef CSM_FILTER_BOOLEANNODE_H
|
||||||
|
#define CSM_FILTER_BOOLEANNODE_H
|
||||||
|
|
||||||
|
#include "leafnode.hpp"
|
||||||
|
|
||||||
|
namespace CSMFilter
|
||||||
|
{
|
||||||
|
class BooleanNode : public LeafNode
|
||||||
|
{
|
||||||
|
bool mTrue;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
BooleanNode (bool true_);
|
||||||
|
|
||||||
|
virtual bool test (const CSMWorld::IdTable& table, int row,
|
||||||
|
const std::map<int, int>& columns) const;
|
||||||
|
///< \return Can the specified table row pass through to filter?
|
||||||
|
/// \param columns column ID to column index mapping
|
||||||
|
|
||||||
|
virtual std::string toString (bool numericColumns) const;
|
||||||
|
///< Return a string that represents this node.
|
||||||
|
///
|
||||||
|
/// \param numericColumns Use numeric IDs instead of string to represent columns.
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
#include "leafnode.hpp"
|
||||||
|
|
||||||
|
std::vector<int> CSMFilter::LeafNode::getReferencedColumns() const
|
||||||
|
{
|
||||||
|
return std::vector<int>();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
|||||||
|
#ifndef CSM_FILTER_LEAFNODE_H
|
||||||
|
#define CSM_FILTER_LEAFNODE_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "node.hpp"
|
||||||
|
|
||||||
|
namespace CSMFilter
|
||||||
|
{
|
||||||
|
class LeafNode : public Node
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual std::vector<int> getReferencedColumns() const;
|
||||||
|
///< Return a list of the IDs of the columns referenced by this node. The column mapping
|
||||||
|
/// passed into test as columns must contain all columns listed here.
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,60 @@
|
|||||||
|
|
||||||
|
#include "narynode.hpp"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
CSMFilter::NAryNode::NAryNode (const std::vector<boost::shared_ptr<Node> >& nodes,
|
||||||
|
const std::string& name)
|
||||||
|
: mNodes (nodes), mName (name)
|
||||||
|
{}
|
||||||
|
|
||||||
|
int CSMFilter::NAryNode::getSize() const
|
||||||
|
{
|
||||||
|
return mNodes.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
const CSMFilter::Node& CSMFilter::NAryNode::operator[] (int index) const
|
||||||
|
{
|
||||||
|
return *mNodes.at (index);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> CSMFilter::NAryNode::getReferencedColumns() const
|
||||||
|
{
|
||||||
|
std::vector<int> columns;
|
||||||
|
|
||||||
|
for (std::vector<boost::shared_ptr<Node> >::const_iterator iter (mNodes.begin());
|
||||||
|
iter!=mNodes.end(); ++iter)
|
||||||
|
{
|
||||||
|
std::vector<int> columns2 = (*iter)->getReferencedColumns();
|
||||||
|
|
||||||
|
columns.insert (columns.end(), columns2.begin(), columns2.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
return columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CSMFilter::NAryNode::toString (bool numericColumns) const
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
|
||||||
|
stream << mName << " (";
|
||||||
|
|
||||||
|
bool first = true;
|
||||||
|
int size = getSize();
|
||||||
|
|
||||||
|
for (int i=0; i<size; ++i)
|
||||||
|
{
|
||||||
|
if (first)
|
||||||
|
first = false;
|
||||||
|
else
|
||||||
|
stream << ", ";
|
||||||
|
|
||||||
|
stream << (*this)[i].toString (numericColumns);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream << ")";
|
||||||
|
|
||||||
|
return stream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
|||||||
|
#ifndef CSM_FILTER_NARYNODE_H
|
||||||
|
#define CSM_FILTER_NARYNODE_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
|
#include "node.hpp"
|
||||||
|
|
||||||
|
namespace CSMFilter
|
||||||
|
{
|
||||||
|
class NAryNode : public Node
|
||||||
|
{
|
||||||
|
std::vector<boost::shared_ptr<Node> > mNodes;
|
||||||
|
std::string mName;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
NAryNode (const std::vector<boost::shared_ptr<Node> >& nodes, const std::string& name);
|
||||||
|
|
||||||
|
int getSize() const;
|
||||||
|
|
||||||
|
const Node& operator[] (int index) const;
|
||||||
|
|
||||||
|
virtual std::vector<int> getReferencedColumns() const;
|
||||||
|
///< Return a list of the IDs of the columns referenced by this node. The column mapping
|
||||||
|
/// passed into test as columns must contain all columns listed here.
|
||||||
|
|
||||||
|
virtual std::string toString (bool numericColumns) const;
|
||||||
|
///< Return a string that represents this node.
|
||||||
|
///
|
||||||
|
/// \param numericColumns Use numeric IDs instead of string to represent columns.
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
#include "node.hpp"
|
||||||
|
|
||||||
|
CSMFilter::Node::Node() {}
|
||||||
|
|
||||||
|
CSMFilter::Node::~Node() {}
|
@ -0,0 +1,53 @@
|
|||||||
|
#ifndef CSM_FILTER_NODE_H
|
||||||
|
#define CSM_FILTER_NODE_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
|
#include <QMetaType>
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
class IdTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CSMFilter
|
||||||
|
{
|
||||||
|
/// \brief Root class for the filter node hierarchy
|
||||||
|
///
|
||||||
|
/// \note When the function documentation for this class mentions "this node", this should be
|
||||||
|
/// interpreted as "the node and all its children".
|
||||||
|
class Node
|
||||||
|
{
|
||||||
|
// not implemented
|
||||||
|
Node (const Node&);
|
||||||
|
Node& operator= (const Node&);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Node();
|
||||||
|
|
||||||
|
virtual ~Node();
|
||||||
|
|
||||||
|
virtual bool test (const CSMWorld::IdTable& table, int row,
|
||||||
|
const std::map<int, int>& columns) const = 0;
|
||||||
|
///< \return Can the specified table row pass through to filter?
|
||||||
|
/// \param columns column ID to column index mapping
|
||||||
|
|
||||||
|
virtual std::vector<int> getReferencedColumns() const = 0;
|
||||||
|
///< Return a list of the IDs of the columns referenced by this node. The column mapping
|
||||||
|
/// passed into test as columns must contain all columns listed here.
|
||||||
|
|
||||||
|
virtual std::string toString (bool numericColumns) const = 0;
|
||||||
|
///< Return a string that represents this node.
|
||||||
|
///
|
||||||
|
/// \param numericColumns Use numeric IDs instead of string to represent columns.
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE (boost::shared_ptr<CSMFilter::Node>)
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
#include "notnode.hpp"
|
||||||
|
|
||||||
|
CSMFilter::NotNode::NotNode (boost::shared_ptr<Node> child) : UnaryNode (child, "not") {}
|
||||||
|
|
||||||
|
bool CSMFilter::NotNode::test (const CSMWorld::IdTable& table, int row,
|
||||||
|
const std::map<int, int>& columns) const
|
||||||
|
{
|
||||||
|
return !getChild().test (table, row, columns);
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
#ifndef CSM_FILTER_NOTNODE_H
|
||||||
|
#define CSM_FILTER_NOTNODE_H
|
||||||
|
|
||||||
|
#include "unarynode.hpp"
|
||||||
|
|
||||||
|
namespace CSMFilter
|
||||||
|
{
|
||||||
|
class NotNode : public UnaryNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
NotNode (boost::shared_ptr<Node> child);
|
||||||
|
|
||||||
|
virtual bool test (const CSMWorld::IdTable& table, int row,
|
||||||
|
const std::map<int, int>& columns) const;
|
||||||
|
///< \return Can the specified table row pass through to filter?
|
||||||
|
/// \param columns column ID to column index mapping
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
#include "ornode.hpp"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
CSMFilter::OrNode::OrNode (const std::vector<boost::shared_ptr<Node> >& nodes)
|
||||||
|
: NAryNode (nodes, "or")
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool CSMFilter::OrNode::test (const CSMWorld::IdTable& table, int row,
|
||||||
|
const std::map<int, int>& columns) const
|
||||||
|
{
|
||||||
|
int size = getSize();
|
||||||
|
|
||||||
|
for (int i=0; i<size; ++i)
|
||||||
|
if ((*this)[i].test (table, row, columns))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
#ifndef CSM_FILTER_ORNODE_H
|
||||||
|
#define CSM_FILTER_ORNODE_H
|
||||||
|
|
||||||
|
#include "narynode.hpp"
|
||||||
|
|
||||||
|
namespace CSMFilter
|
||||||
|
{
|
||||||
|
class OrNode : public NAryNode
|
||||||
|
{
|
||||||
|
bool mAnd;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
OrNode (const std::vector<boost::shared_ptr<Node> >& nodes);
|
||||||
|
|
||||||
|
virtual bool test (const CSMWorld::IdTable& table, int row,
|
||||||
|
const std::map<int, int>& columns) const;
|
||||||
|
///< \return Can the specified table row pass through to filter?
|
||||||
|
/// \param columns column ID to column index mapping
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,544 @@
|
|||||||
|
|
||||||
|
#include "parser.hpp"
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
|
#include "../world/columns.hpp"
|
||||||
|
|
||||||
|
#include "booleannode.hpp"
|
||||||
|
#include "ornode.hpp"
|
||||||
|
#include "andnode.hpp"
|
||||||
|
#include "notnode.hpp"
|
||||||
|
#include "textnode.hpp"
|
||||||
|
#include "valuenode.hpp"
|
||||||
|
|
||||||
|
namespace CSMFilter
|
||||||
|
{
|
||||||
|
struct Token
|
||||||
|
{
|
||||||
|
enum Type
|
||||||
|
{
|
||||||
|
Type_EOS,
|
||||||
|
Type_None,
|
||||||
|
Type_String,
|
||||||
|
Type_Number,
|
||||||
|
Type_Open,
|
||||||
|
Type_Close,
|
||||||
|
Type_OpenSquare,
|
||||||
|
Type_CloseSquare,
|
||||||
|
Type_Comma,
|
||||||
|
Type_Keyword_True, ///< \attention Keyword enums must be arranged continuously.
|
||||||
|
Type_Keyword_False,
|
||||||
|
Type_Keyword_And,
|
||||||
|
Type_Keyword_Or,
|
||||||
|
Type_Keyword_Not,
|
||||||
|
Type_Keyword_Text,
|
||||||
|
Type_Keyword_Value
|
||||||
|
};
|
||||||
|
|
||||||
|
Type mType;
|
||||||
|
std::string mString;
|
||||||
|
double mNumber;
|
||||||
|
|
||||||
|
Token (Type type);
|
||||||
|
|
||||||
|
Token (const std::string& string);
|
||||||
|
|
||||||
|
Token (double number);
|
||||||
|
|
||||||
|
operator bool() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
Token::Token (Type type) : mType (type) {}
|
||||||
|
|
||||||
|
Token::Token (const std::string& string) : mType (Type_String), mString (string) {}
|
||||||
|
|
||||||
|
Token::Token (double number) : mType (Type_Number), mNumber (number) {}
|
||||||
|
|
||||||
|
Token::operator bool() const
|
||||||
|
{
|
||||||
|
return mType!=Type_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator== (const Token& left, const Token& right)
|
||||||
|
{
|
||||||
|
if (left.mType!=right.mType)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (left.mType)
|
||||||
|
{
|
||||||
|
case Token::Type_String: return left.mString==right.mString;
|
||||||
|
case Token::Type_Number: return left.mNumber==right.mNumber;
|
||||||
|
|
||||||
|
default: return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMFilter::Token CSMFilter::Parser::getStringToken()
|
||||||
|
{
|
||||||
|
std::string string;
|
||||||
|
|
||||||
|
int size = static_cast<int> (mInput.size());
|
||||||
|
|
||||||
|
for (; mIndex<size; ++mIndex)
|
||||||
|
{
|
||||||
|
char c = mInput[mIndex];
|
||||||
|
|
||||||
|
if (std::isalpha (c) || c=='_' || (!string.empty() && std::isdigit (c)) || c=='"' ||
|
||||||
|
(!string.empty() && string[0]=='"'))
|
||||||
|
string += c;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (c=='"' && string.size()>1)
|
||||||
|
{
|
||||||
|
++mIndex;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!string.empty())
|
||||||
|
{
|
||||||
|
if (string[0]=='"' && (string[string.size()-1]!='"' || string.size()<2) )
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return Token (Token::Type_None);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string[0]!='"' && string[string.size()-1]=='"')
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return Token (Token::Type_None);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string[0]=='"')
|
||||||
|
string = string.substr (1, string.size()-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return checkKeywords (string);
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMFilter::Token CSMFilter::Parser::getNumberToken()
|
||||||
|
{
|
||||||
|
std::string string;
|
||||||
|
|
||||||
|
int size = static_cast<int> (mInput.size());
|
||||||
|
|
||||||
|
bool hasDecimalPoint = false;
|
||||||
|
bool hasDigit = false;
|
||||||
|
|
||||||
|
for (; mIndex<size; ++mIndex)
|
||||||
|
{
|
||||||
|
char c = mInput[mIndex];
|
||||||
|
|
||||||
|
if (std::isdigit (c))
|
||||||
|
{
|
||||||
|
string += c;
|
||||||
|
hasDigit = true;
|
||||||
|
}
|
||||||
|
else if (c=='.' && !hasDecimalPoint)
|
||||||
|
{
|
||||||
|
string += c;
|
||||||
|
hasDecimalPoint = true;
|
||||||
|
}
|
||||||
|
else if (string.empty() && c=='-')
|
||||||
|
string += c;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasDigit)
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return Token (Token::Type_None);
|
||||||
|
}
|
||||||
|
|
||||||
|
float value;
|
||||||
|
std::istringstream stream (string.c_str());
|
||||||
|
stream >> value;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMFilter::Token CSMFilter::Parser::checkKeywords (const Token& token)
|
||||||
|
{
|
||||||
|
static const char *sKeywords[] =
|
||||||
|
{
|
||||||
|
"true", "false",
|
||||||
|
"and", "or", "not",
|
||||||
|
"text", "value",
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string string = Misc::StringUtils::lowerCase (token.mString);
|
||||||
|
|
||||||
|
for (int i=0; sKeywords[i]; ++i)
|
||||||
|
if (sKeywords[i]==string)
|
||||||
|
return Token (static_cast<Token::Type> (i+Token::Type_Keyword_True));
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMFilter::Token CSMFilter::Parser::getNextToken()
|
||||||
|
{
|
||||||
|
int size = static_cast<int> (mInput.size());
|
||||||
|
|
||||||
|
char c = 0;
|
||||||
|
|
||||||
|
for (; mIndex<size; ++mIndex)
|
||||||
|
{
|
||||||
|
c = mInput[mIndex];
|
||||||
|
|
||||||
|
if (c!=' ')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mIndex>=size)
|
||||||
|
return Token (Token::Type_EOS);
|
||||||
|
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case '(': ++mIndex; return Token (Token::Type_Open);
|
||||||
|
case ')': ++mIndex; return Token (Token::Type_Close);
|
||||||
|
case '[': ++mIndex; return Token (Token::Type_OpenSquare);
|
||||||
|
case ']': ++mIndex; return Token (Token::Type_CloseSquare);
|
||||||
|
case ',': ++mIndex; return Token (Token::Type_Comma);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c=='"' || c=='_' || std::isalpha (c))
|
||||||
|
return getStringToken();
|
||||||
|
|
||||||
|
if (c=='-' || c=='.' || std::isdigit (c))
|
||||||
|
return getNumberToken();
|
||||||
|
|
||||||
|
error();
|
||||||
|
return Token (Token::Type_None);
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseImp (bool allowEmpty)
|
||||||
|
{
|
||||||
|
if (Token token = getNextToken())
|
||||||
|
{
|
||||||
|
switch (token.mType)
|
||||||
|
{
|
||||||
|
case Token::Type_Keyword_True:
|
||||||
|
|
||||||
|
return boost::shared_ptr<CSMFilter::Node> (new BooleanNode (true));
|
||||||
|
|
||||||
|
case Token::Type_Keyword_False:
|
||||||
|
|
||||||
|
return boost::shared_ptr<CSMFilter::Node> (new BooleanNode (false));
|
||||||
|
|
||||||
|
case Token::Type_Keyword_And:
|
||||||
|
case Token::Type_Keyword_Or:
|
||||||
|
|
||||||
|
return parseNAry (token);
|
||||||
|
|
||||||
|
case Token::Type_Keyword_Not:
|
||||||
|
{
|
||||||
|
boost::shared_ptr<CSMFilter::Node> node = parseImp();
|
||||||
|
|
||||||
|
if (mError)
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
|
||||||
|
return boost::shared_ptr<CSMFilter::Node> (new NotNode (node));
|
||||||
|
}
|
||||||
|
|
||||||
|
case Token::Type_Keyword_Text:
|
||||||
|
|
||||||
|
return parseText();
|
||||||
|
|
||||||
|
case Token::Type_Keyword_Value:
|
||||||
|
|
||||||
|
return parseValue();
|
||||||
|
|
||||||
|
case Token::Type_EOS:
|
||||||
|
|
||||||
|
if (!allowEmpty)
|
||||||
|
error();
|
||||||
|
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseNAry (const Token& keyword)
|
||||||
|
{
|
||||||
|
std::vector<boost::shared_ptr<Node> > nodes;
|
||||||
|
|
||||||
|
Token token = getNextToken();
|
||||||
|
|
||||||
|
if (token.mType!=Token::Type_Open)
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
boost::shared_ptr<Node> node = parseImp();
|
||||||
|
|
||||||
|
if (mError)
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
|
||||||
|
nodes.push_back (node);
|
||||||
|
|
||||||
|
Token token = getNextToken();
|
||||||
|
|
||||||
|
if (!token || (token.mType!=Token::Type_Close && token.mType!=Token::Type_Comma))
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token.mType==Token::Type_Close)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodes.empty())
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (keyword.mType)
|
||||||
|
{
|
||||||
|
case Token::Type_Keyword_And: return boost::shared_ptr<CSMFilter::Node> (new AndNode (nodes));
|
||||||
|
case Token::Type_Keyword_Or: return boost::shared_ptr<CSMFilter::Node> (new OrNode (nodes));
|
||||||
|
default: error(); return boost::shared_ptr<Node>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseText()
|
||||||
|
{
|
||||||
|
Token token = getNextToken();
|
||||||
|
|
||||||
|
if (token.mType!=Token::Type_Open)
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
}
|
||||||
|
|
||||||
|
token = getNextToken();
|
||||||
|
|
||||||
|
if (!token)
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
|
||||||
|
// parse column ID
|
||||||
|
int columnId = -1;
|
||||||
|
|
||||||
|
if (token.mType==Token::Type_Number)
|
||||||
|
{
|
||||||
|
if (static_cast<int> (token.mNumber)==token.mNumber)
|
||||||
|
columnId = static_cast<int> (token.mNumber);
|
||||||
|
}
|
||||||
|
else if (token.mType==Token::Type_String)
|
||||||
|
{
|
||||||
|
columnId = CSMWorld::Columns::getId (token.mString);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (columnId<0)
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
}
|
||||||
|
|
||||||
|
token = getNextToken();
|
||||||
|
|
||||||
|
if (token.mType!=Token::Type_Comma)
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse text pattern
|
||||||
|
token = getNextToken();
|
||||||
|
|
||||||
|
if (token.mType!=Token::Type_String)
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string text = token.mString;
|
||||||
|
|
||||||
|
token = getNextToken();
|
||||||
|
|
||||||
|
if (token.mType!=Token::Type_Close)
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return boost::shared_ptr<Node> (new TextNode (columnId, text));
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseValue()
|
||||||
|
{
|
||||||
|
Token token = getNextToken();
|
||||||
|
|
||||||
|
if (token.mType!=Token::Type_Open)
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
}
|
||||||
|
|
||||||
|
token = getNextToken();
|
||||||
|
|
||||||
|
if (!token)
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
|
||||||
|
// parse column ID
|
||||||
|
int columnId = -1;
|
||||||
|
|
||||||
|
if (token.mType==Token::Type_Number)
|
||||||
|
{
|
||||||
|
if (static_cast<int> (token.mNumber)==token.mNumber)
|
||||||
|
columnId = static_cast<int> (token.mNumber);
|
||||||
|
}
|
||||||
|
else if (token.mType==Token::Type_String)
|
||||||
|
{
|
||||||
|
columnId = CSMWorld::Columns::getId (token.mString);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (columnId<0)
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
}
|
||||||
|
|
||||||
|
token = getNextToken();
|
||||||
|
|
||||||
|
if (token.mType!=Token::Type_Comma)
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse value
|
||||||
|
double lower = 0;
|
||||||
|
double upper = 0;
|
||||||
|
bool min = false;
|
||||||
|
bool max = false;
|
||||||
|
|
||||||
|
token = getNextToken();
|
||||||
|
|
||||||
|
if (token.mType==Token::Type_Number)
|
||||||
|
{
|
||||||
|
// single value
|
||||||
|
min = max = true;
|
||||||
|
lower = upper = token.mNumber;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// interval
|
||||||
|
if (token.mType==Token::Type_OpenSquare)
|
||||||
|
min = true;
|
||||||
|
else if (token.mType!=Token::Type_CloseSquare && token.mType!=Token::Type_Open)
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
}
|
||||||
|
|
||||||
|
token = getNextToken();
|
||||||
|
|
||||||
|
if (token.mType!=Token::Type_Number)
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
}
|
||||||
|
|
||||||
|
lower = token.mNumber;
|
||||||
|
|
||||||
|
token = getNextToken();
|
||||||
|
|
||||||
|
if (token.mType!=Token::Type_Comma)
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
}
|
||||||
|
|
||||||
|
token = getNextToken();
|
||||||
|
|
||||||
|
if (token.mType!=Token::Type_Number)
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
}
|
||||||
|
|
||||||
|
upper = token.mNumber;
|
||||||
|
|
||||||
|
token = getNextToken();
|
||||||
|
|
||||||
|
if (token.mType==Token::Type_CloseSquare)
|
||||||
|
max = true;
|
||||||
|
else if (token.mType!=Token::Type_OpenSquare && token.mType!=Token::Type_Close)
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token = getNextToken();
|
||||||
|
|
||||||
|
if (token.mType!=Token::Type_Close)
|
||||||
|
{
|
||||||
|
error();
|
||||||
|
return boost::shared_ptr<Node>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return boost::shared_ptr<Node> (new ValueNode (columnId, lower, upper, min, max));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMFilter::Parser::error()
|
||||||
|
{
|
||||||
|
mError = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMFilter::Parser::Parser() : mIndex (0), mError (false) {}
|
||||||
|
|
||||||
|
bool CSMFilter::Parser::parse (const std::string& filter)
|
||||||
|
{
|
||||||
|
// reset
|
||||||
|
mFilter.reset();
|
||||||
|
mError = false;
|
||||||
|
mInput = filter;
|
||||||
|
mIndex = 0;
|
||||||
|
|
||||||
|
boost::shared_ptr<Node> node = parseImp (true);
|
||||||
|
|
||||||
|
if (mError)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (getNextToken()!=Token (Token::Type_EOS))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (node)
|
||||||
|
mFilter = node;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Empty filter string equals to filter "true".
|
||||||
|
mFilter.reset (new BooleanNode (true));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::getFilter() const
|
||||||
|
{
|
||||||
|
if (mError)
|
||||||
|
throw std::logic_error ("No filter available");
|
||||||
|
|
||||||
|
return mFilter;
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
#ifndef CSM_FILTER_PARSER_H
|
||||||
|
#define CSM_FILTER_PARSER_H
|
||||||
|
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
|
#include "node.hpp"
|
||||||
|
|
||||||
|
namespace CSMFilter
|
||||||
|
{
|
||||||
|
struct Token;
|
||||||
|
|
||||||
|
class Parser
|
||||||
|
{
|
||||||
|
boost::shared_ptr<Node> mFilter;
|
||||||
|
std::string mInput;
|
||||||
|
int mIndex;
|
||||||
|
bool mError;
|
||||||
|
|
||||||
|
Token getStringToken();
|
||||||
|
|
||||||
|
Token getNumberToken();
|
||||||
|
|
||||||
|
Token getNextToken();
|
||||||
|
|
||||||
|
Token checkKeywords (const Token& token);
|
||||||
|
///< Turn string token into keyword token, if possible.
|
||||||
|
|
||||||
|
boost::shared_ptr<Node> parseImp (bool allowEmpty = false);
|
||||||
|
///< Will return a null-pointer, if there is nothing more to parse.
|
||||||
|
|
||||||
|
boost::shared_ptr<Node> parseNAry (const Token& keyword);
|
||||||
|
|
||||||
|
boost::shared_ptr<Node> parseText();
|
||||||
|
|
||||||
|
boost::shared_ptr<Node> parseValue();
|
||||||
|
|
||||||
|
void error();
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Parser();
|
||||||
|
|
||||||
|
bool parse (const std::string& filter);
|
||||||
|
///< Discards any previous calls to parse
|
||||||
|
///
|
||||||
|
/// \return Success?
|
||||||
|
|
||||||
|
boost::shared_ptr<Node> getFilter() const;
|
||||||
|
///< Throws an exception if the last call to parse did not return true.
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,62 @@
|
|||||||
|
|
||||||
|
#include "textnode.hpp"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <QRegExp>
|
||||||
|
|
||||||
|
#include "../world/columns.hpp"
|
||||||
|
#include "../world/idtable.hpp"
|
||||||
|
|
||||||
|
CSMFilter::TextNode::TextNode (int columnId, const std::string& text)
|
||||||
|
: mColumnId (columnId), mText (text)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool CSMFilter::TextNode::test (const CSMWorld::IdTable& table, int row,
|
||||||
|
const std::map<int, int>& columns) const
|
||||||
|
{
|
||||||
|
const std::map<int, int>::const_iterator iter = columns.find (mColumnId);
|
||||||
|
|
||||||
|
if (iter==columns.end())
|
||||||
|
throw std::logic_error ("invalid column in text node test");
|
||||||
|
|
||||||
|
if (iter->second==-1)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
QModelIndex index = table.index (row, iter->second);
|
||||||
|
|
||||||
|
QVariant data = table.data (index);
|
||||||
|
|
||||||
|
if (data.type()!=QVariant::String)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/// \todo make pattern syntax configurable
|
||||||
|
QRegExp regExp (QString::fromUtf8 (mText.c_str()), Qt::CaseInsensitive);
|
||||||
|
|
||||||
|
return regExp.exactMatch (data.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> CSMFilter::TextNode::getReferencedColumns() const
|
||||||
|
{
|
||||||
|
return std::vector<int> (1, mColumnId);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CSMFilter::TextNode::toString (bool numericColumns) const
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
|
||||||
|
stream << "text (";
|
||||||
|
|
||||||
|
if (numericColumns)
|
||||||
|
stream << mColumnId;
|
||||||
|
else
|
||||||
|
stream
|
||||||
|
<< "\""
|
||||||
|
<< CSMWorld::Columns::getName (static_cast<CSMWorld::Columns::ColumnId> (mColumnId))
|
||||||
|
<< "\"";
|
||||||
|
|
||||||
|
stream << ", \"" << mText << "\")";
|
||||||
|
|
||||||
|
return stream.str();
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
#ifndef CSM_FILTER_TEXTNODE_H
|
||||||
|
#define CSM_FILTER_TEXTNODE_H
|
||||||
|
|
||||||
|
#include "leafnode.hpp"
|
||||||
|
|
||||||
|
namespace CSMFilter
|
||||||
|
{
|
||||||
|
class TextNode : public LeafNode
|
||||||
|
{
|
||||||
|
int mColumnId;
|
||||||
|
std::string mText;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
TextNode (int columnId, const std::string& text);
|
||||||
|
|
||||||
|
virtual bool test (const CSMWorld::IdTable& table, int row,
|
||||||
|
const std::map<int, int>& columns) const;
|
||||||
|
///< \return Can the specified table row pass through to filter?
|
||||||
|
/// \param columns column ID to column index mapping
|
||||||
|
|
||||||
|
virtual std::vector<int> getReferencedColumns() const;
|
||||||
|
///< Return a list of the IDs of the columns referenced by this node. The column mapping
|
||||||
|
/// passed into test as columns must contain all columns listed here.
|
||||||
|
|
||||||
|
virtual std::string toString (bool numericColumns) const;
|
||||||
|
///< Return a string that represents this node.
|
||||||
|
///
|
||||||
|
/// \param numericColumns Use numeric IDs instead of string to represent columns.
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
#include "unarynode.hpp"
|
||||||
|
|
||||||
|
CSMFilter::UnaryNode::UnaryNode (boost::shared_ptr<Node> child, const std::string& name)
|
||||||
|
: mChild (child), mName (name)
|
||||||
|
{}
|
||||||
|
|
||||||
|
const CSMFilter::Node& CSMFilter::UnaryNode::getChild() const
|
||||||
|
{
|
||||||
|
return *mChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMFilter::Node& CSMFilter::UnaryNode::getChild()
|
||||||
|
{
|
||||||
|
return *mChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> CSMFilter::UnaryNode::getReferencedColumns() const
|
||||||
|
{
|
||||||
|
return mChild->getReferencedColumns();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CSMFilter::UnaryNode::toString (bool numericColumns) const
|
||||||
|
{
|
||||||
|
return mName + " " + mChild->toString (numericColumns);
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
#ifndef CSM_FILTER_UNARYNODE_H
|
||||||
|
#define CSM_FILTER_UNARYNODE_H
|
||||||
|
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
|
#include "node.hpp"
|
||||||
|
|
||||||
|
namespace CSMFilter
|
||||||
|
{
|
||||||
|
class UnaryNode : public Node
|
||||||
|
{
|
||||||
|
boost::shared_ptr<Node> mChild;
|
||||||
|
std::string mName;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
UnaryNode (boost::shared_ptr<Node> child, const std::string& name);
|
||||||
|
|
||||||
|
const Node& getChild() const;
|
||||||
|
|
||||||
|
Node& getChild();
|
||||||
|
|
||||||
|
virtual std::vector<int> getReferencedColumns() const;
|
||||||
|
///< Return a list of the IDs of the columns referenced by this node. The column mapping
|
||||||
|
/// passed into test as columns must contain all columns listed here.
|
||||||
|
|
||||||
|
virtual std::string toString (bool numericColumns) const;
|
||||||
|
///< Return a string that represents this node.
|
||||||
|
///
|
||||||
|
/// \param numericColumns Use numeric IDs instead of string to represent columns.
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,71 @@
|
|||||||
|
|
||||||
|
#include "valuenode.hpp"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "../world/columns.hpp"
|
||||||
|
#include "../world/idtable.hpp"
|
||||||
|
|
||||||
|
CSMFilter::ValueNode::ValueNode (int columnId,
|
||||||
|
double lower, double upper, bool min, bool max)
|
||||||
|
: mColumnId (columnId), mLower (lower), mUpper (upper), mMin (min), mMax (max)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool CSMFilter::ValueNode::test (const CSMWorld::IdTable& table, int row,
|
||||||
|
const std::map<int, int>& columns) const
|
||||||
|
{
|
||||||
|
const std::map<int, int>::const_iterator iter = columns.find (mColumnId);
|
||||||
|
|
||||||
|
if (iter==columns.end())
|
||||||
|
throw std::logic_error ("invalid column in test value test");
|
||||||
|
|
||||||
|
if (iter->second==-1)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
QModelIndex index = table.index (row, iter->second);
|
||||||
|
|
||||||
|
QVariant data = table.data (index);
|
||||||
|
|
||||||
|
if (data.type()!=QVariant::Double && data.type()!=QVariant::Bool && data.type()!=QVariant::Int &&
|
||||||
|
data.type()!=QVariant::UInt)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
double value = data.toDouble();
|
||||||
|
|
||||||
|
if (mLower==mUpper && mMin && mMax)
|
||||||
|
return value==mLower;
|
||||||
|
|
||||||
|
return (mMin ? value>=mLower : value>mLower) && (mMax ? value<=mUpper : value<mUpper);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> CSMFilter::ValueNode::getReferencedColumns() const
|
||||||
|
{
|
||||||
|
return std::vector<int> (1, mColumnId);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CSMFilter::ValueNode::toString (bool numericColumns) const
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
|
||||||
|
stream << "value (";
|
||||||
|
|
||||||
|
if (numericColumns)
|
||||||
|
stream << mColumnId;
|
||||||
|
else
|
||||||
|
stream
|
||||||
|
<< "\""
|
||||||
|
<< CSMWorld::Columns::getName (static_cast<CSMWorld::Columns::ColumnId> (mColumnId))
|
||||||
|
<< "\"";
|
||||||
|
|
||||||
|
stream << ", \"";
|
||||||
|
|
||||||
|
if (mLower==mUpper && mMin && mMax)
|
||||||
|
stream << mLower;
|
||||||
|
else
|
||||||
|
stream << (mMin ? "[" : "(") << mLower << ", " << mUpper << (mMax ? "]" : ")");
|
||||||
|
|
||||||
|
stream << ")";
|
||||||
|
|
||||||
|
return stream.str();
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
#ifndef CSM_FILTER_VALUENODE_H
|
||||||
|
#define CSM_FILTER_VALUENODE_H
|
||||||
|
|
||||||
|
#include "leafnode.hpp"
|
||||||
|
|
||||||
|
namespace CSMFilter
|
||||||
|
{
|
||||||
|
class ValueNode : public LeafNode
|
||||||
|
{
|
||||||
|
int mColumnId;
|
||||||
|
std::string mText;
|
||||||
|
double mLower;
|
||||||
|
double mUpper;
|
||||||
|
bool mMin;
|
||||||
|
bool mMax;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
ValueNode (int columnId, double lower, double upper, bool min, bool max);
|
||||||
|
|
||||||
|
virtual bool test (const CSMWorld::IdTable& table, int row,
|
||||||
|
const std::map<int, int>& columns) const;
|
||||||
|
///< \return Can the specified table row pass through to filter?
|
||||||
|
/// \param columns column ID to column index mapping
|
||||||
|
|
||||||
|
virtual std::vector<int> getReferencedColumns() const;
|
||||||
|
///< Return a list of the IDs of the columns referenced by this node. The column mapping
|
||||||
|
/// passed into test as columns must contain all columns listed here.
|
||||||
|
|
||||||
|
virtual std::string toString (bool numericColumns) const;
|
||||||
|
///< Return a string that represents this node.
|
||||||
|
///
|
||||||
|
/// \param numericColumns Use numeric IDs instead of string to represent columns.
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
#include "editwidget.hpp"
|
||||||
|
|
||||||
|
CSVFilter::EditWidget::EditWidget (QWidget *parent)
|
||||||
|
: QLineEdit (parent)
|
||||||
|
{
|
||||||
|
mPalette = palette();
|
||||||
|
connect (this, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVFilter::EditWidget::textChanged (const QString& text)
|
||||||
|
{
|
||||||
|
if (mParser.parse (text.toUtf8().constData()))
|
||||||
|
{
|
||||||
|
setPalette (mPalette);
|
||||||
|
emit filterChanged (mParser.getFilter());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QPalette palette (mPalette);
|
||||||
|
palette.setColor (QPalette::Text, Qt::red);
|
||||||
|
setPalette (palette);
|
||||||
|
|
||||||
|
/// \todo improve error reporting; mark only the faulty part
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
#ifndef CSV_FILTER_EDITWIDGET_H
|
||||||
|
#define CSV_FILTER_EDITWIDGET_H
|
||||||
|
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QPalette>
|
||||||
|
|
||||||
|
#include "../../model/filter/parser.hpp"
|
||||||
|
#include "../../model/filter/node.hpp"
|
||||||
|
|
||||||
|
namespace CSVFilter
|
||||||
|
{
|
||||||
|
class EditWidget : public QLineEdit
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
CSMFilter::Parser mParser;
|
||||||
|
QPalette mPalette;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
EditWidget (QWidget *parent = 0);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void filterChanged (boost::shared_ptr<CSMFilter::Node> filter);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
void textChanged (const QString& text);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,24 @@
|
|||||||
|
|
||||||
|
#include "filterbox.hpp"
|
||||||
|
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
|
||||||
|
#include "recordfilterbox.hpp"
|
||||||
|
|
||||||
|
CSVFilter::FilterBox::FilterBox (QWidget *parent)
|
||||||
|
: QWidget (parent)
|
||||||
|
{
|
||||||
|
QHBoxLayout *layout = new QHBoxLayout (this);
|
||||||
|
|
||||||
|
layout->setContentsMargins (0, 0, 0, 0);
|
||||||
|
|
||||||
|
RecordFilterBox *recordFilterBox = new RecordFilterBox (this);
|
||||||
|
|
||||||
|
layout->addWidget (recordFilterBox);
|
||||||
|
|
||||||
|
setLayout (layout);
|
||||||
|
|
||||||
|
connect (recordFilterBox,
|
||||||
|
SIGNAL (filterChanged (boost::shared_ptr<CSMFilter::Node>)),
|
||||||
|
this, SIGNAL (recordFilterChanged (boost::shared_ptr<CSMFilter::Node>)));
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef CSV_FILTER_FILTERBOX_H
|
||||||
|
#define CSV_FILTER_FILTERBOX_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "../../model/filter/node.hpp"
|
||||||
|
|
||||||
|
namespace CSVFilter
|
||||||
|
{
|
||||||
|
class FilterBox : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
FilterBox (QWidget *parent = 0);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void recordFilterChanged (boost::shared_ptr<CSMFilter::Node> filter);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,63 @@
|
|||||||
|
|
||||||
|
#include "filtercreator.hpp"
|
||||||
|
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QLabel>
|
||||||
|
|
||||||
|
#include "../../model/filter/filter.hpp"
|
||||||
|
|
||||||
|
std::string CSVFilter::FilterCreator::getNamespace() const
|
||||||
|
{
|
||||||
|
switch (mScope->currentIndex())
|
||||||
|
{
|
||||||
|
case CSMFilter::Filter::Scope_Project: return "project::";
|
||||||
|
case CSMFilter::Filter::Scope_Session: return "session::";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVFilter::FilterCreator::update()
|
||||||
|
{
|
||||||
|
mNamespace->setText (QString::fromUtf8 (getNamespace().c_str()));
|
||||||
|
GenericCreator::update();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CSVFilter::FilterCreator::getId() const
|
||||||
|
{
|
||||||
|
return getNamespace() + GenericCreator::getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
CSVFilter::FilterCreator::FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack,
|
||||||
|
const CSMWorld::UniversalId& id)
|
||||||
|
: GenericCreator (data, undoStack, id)
|
||||||
|
{
|
||||||
|
mNamespace = new QLabel ("::", this);
|
||||||
|
insertAtBeginning (mNamespace, false);
|
||||||
|
|
||||||
|
mScope = new QComboBox (this);
|
||||||
|
|
||||||
|
mScope->addItem ("Project");
|
||||||
|
mScope->addItem ("Session");
|
||||||
|
/// \ŧodo re-enable for OpenMW 1.1
|
||||||
|
// mScope->addItem ("Content");
|
||||||
|
|
||||||
|
connect (mScope, SIGNAL (currentIndexChanged (int)), this, SLOT (setScope (int)));
|
||||||
|
|
||||||
|
insertAtBeginning (mScope, false);
|
||||||
|
|
||||||
|
QLabel *label = new QLabel ("Scope", this);
|
||||||
|
insertAtBeginning (label, false);
|
||||||
|
|
||||||
|
mScope->setCurrentIndex (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVFilter::FilterCreator::reset()
|
||||||
|
{
|
||||||
|
GenericCreator::reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVFilter::FilterCreator::setScope (int index)
|
||||||
|
{
|
||||||
|
update();
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
#ifndef CSV_FILTER_FILTERCREATOR_H
|
||||||
|
#define CSV_FILTER_FILTERCREATOR_H
|
||||||
|
|
||||||
|
class QComboBox;
|
||||||
|
class QLabel;
|
||||||
|
|
||||||
|
#include "../world/genericcreator.hpp"
|
||||||
|
|
||||||
|
namespace CSVFilter
|
||||||
|
{
|
||||||
|
class FilterCreator : public CSVWorld::GenericCreator
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
QComboBox *mScope;
|
||||||
|
QLabel *mNamespace;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::string getNamespace() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
void update();
|
||||||
|
|
||||||
|
virtual std::string getId() const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack,
|
||||||
|
const CSMWorld::UniversalId& id);
|
||||||
|
|
||||||
|
virtual void reset();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
void setScope (int index);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
#include "recordfilterbox.hpp"
|
||||||
|
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
|
||||||
|
#include "editwidget.hpp"
|
||||||
|
|
||||||
|
CSVFilter::RecordFilterBox::RecordFilterBox (QWidget *parent)
|
||||||
|
: QWidget (parent)
|
||||||
|
{
|
||||||
|
QHBoxLayout *layout = new QHBoxLayout (this);
|
||||||
|
|
||||||
|
layout->setContentsMargins (0, 0, 0, 0);
|
||||||
|
|
||||||
|
layout->addWidget (new QLabel ("Record Filter", this));
|
||||||
|
|
||||||
|
EditWidget *editWidget = new EditWidget (this);
|
||||||
|
|
||||||
|
layout->addWidget (editWidget);
|
||||||
|
|
||||||
|
setLayout (layout);
|
||||||
|
|
||||||
|
connect (
|
||||||
|
editWidget, SIGNAL (filterChanged (boost::shared_ptr<CSMFilter::Node>)),
|
||||||
|
this, SIGNAL (filterChanged (boost::shared_ptr<CSMFilter::Node>)));
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
#ifndef CSV_FILTER_RECORDFILTERBOX_H
|
||||||
|
#define CSV_FILTER_RECORDFILTERBOX_H
|
||||||
|
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
|
||||||
|
#include "../../model/filter/node.hpp"
|
||||||
|
|
||||||
|
namespace CSVFilter
|
||||||
|
{
|
||||||
|
class RecordFilterBox : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
RecordFilterBox (QWidget *parent = 0);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void filterChanged (boost::shared_ptr<CSMFilter::Node> filter);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue