From fe2634b472f83c3c84e666be1411320fe4dd4642 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sun, 27 Apr 2014 10:05:05 -0700 Subject: [PATCH] Filter - New Filter object to control filtering, instead of commands/Command.cpp. --- src/CMakeLists.txt | 1 + src/Filter.cpp | 254 +++++++++++++++++++++++++++++++++++++++++++++ src/Filter.h | 50 +++++++++ 3 files changed, 305 insertions(+) create mode 100644 src/Filter.cpp create mode 100644 src/Filter.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index aa4194e97..d29d5279f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,6 +20,7 @@ set (task_SRCS A3.cpp A3.h E9.cpp E9.h Eval.cpp Eval.h File.cpp File.h + Filter.cpp Filter.h Hooks.cpp Hooks.h ISO8601.cpp ISO8601.h JSON.cpp JSON.h diff --git a/src/Filter.cpp b/src/Filter.cpp new file mode 100644 index 000000000..f6d0529ac --- /dev/null +++ b/src/Filter.cpp @@ -0,0 +1,254 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2006 - 2014, Paul Beckingham, Federico Hernandez. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// http://www.opensource.org/licenses/mit-license.php +// +//////////////////////////////////////////////////////////////////////////////// + +#include // TODO Remove +#include +#include +#include +#include +#include +#include +#include + +extern Context context; + +//////////////////////////////////////////////////////////////////////////////// +Filter::Filter () +: _startCount (0) +, _endCount (0) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +Filter::~Filter () +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// Take an input set of tasks and filter into a subset. +void Filter::subset (const std::vector & input, std::vector & output) +{ + context.timer_filter.start (); + + A3 filt = context.a3.extract_filter (); + filt.dump ("extract_filter"); + + if (context.config.getBoolean ("debug")) + { + Tree* t = context.a3t.tree (); + if (t) + context.debug (t->dump ()); + } + + std::string filterExpr = context.a3t.getFilterExpression (); + context.debug ("\033[1;37;42mFILTER\033[0m " + filterExpr); + + if (filterExpr.length ()) + { + E9 e (filt); + + Eval eval; + eval.addSource (namedDates); + // TODO Need DOM source. + eval.compileExpression (filterExpr); + + std::vector ::const_iterator task; + for (task = input.begin (); task != input.end (); ++task) + { + bool oldFilter = e.evalFilter (*task); + if (oldFilter) + output.push_back (*task); + + Variant var; + eval.evaluateCompiledExpression (var); + + if (oldFilter != var.get_bool ()) + std::cout << "# filter mismatch ID " << task->id << " UUID " << task->get ("uuid") << "\n"; + } + } + else + output = input; + + context.timer_filter.stop (); +} + +//////////////////////////////////////////////////////////////////////////////// +// Take the set of all tasks and filter into a subset. +void Filter::subset (std::vector & output) +{ + context.timer_filter.start (); + A3 filt = context.a3.extract_filter (); + filt.dump ("extract_filter"); + + if (context.config.getBoolean ("debug")) + { + Tree* t = context.a3t.tree (); + if (t) + context.debug (t->dump ()); + } + + std::string filterExpr = context.a3t.getFilterExpression (); + context.debug ("\033[1;37;42mFILTER\033[0m " + filterExpr); + + if (filterExpr.length ()) + { + context.timer_filter.stop (); + const std::vector & pending = context.tdb2.pending.get_tasks (); + context.timer_filter.start (); + E9 e (filt); + + Eval eval; + eval.addSource (namedDates); + // TODO Need DOM source. + eval.compileExpression (filterExpr); + + output.clear (); + std::vector ::const_iterator task; + + for (task = pending.begin (); task != pending.end (); ++task) + { + bool oldFilter = e.evalFilter (*task); + if (oldFilter) + output.push_back (*task); + + Variant var; + eval.evaluateCompiledExpression (var); + + if (oldFilter != var.get_bool ()) + std::cout << "# filter mismatch ID " << task->id << " UUID " << task->get ("uuid") << "\n"; + } + + if (! shortcut ()) + { + context.timer_filter.stop (); + const std::vector & completed = context.tdb2.completed.get_tasks (); // TODO Optional + context.timer_filter.start (); + + for (task = completed.begin (); task != completed.end (); ++task) + { + bool oldFilter = e.evalFilter (*task); + if (oldFilter) + output.push_back (*task); + + Variant var; + eval.evaluateCompiledExpression (var); + + if (oldFilter != var.get_bool ()) + std::cout << "# filter mismatch ID " << task->id << " UUID " << task->get ("uuid") << "\n"; + } + } + } + else + { + safety (); + + context.timer_filter.stop (); + const std::vector & pending = context.tdb2.pending.get_tasks (); + const std::vector & completed = context.tdb2.completed.get_tasks (); + context.timer_filter.start (); + + std::vector ::const_iterator task; + for (task = pending.begin (); task != pending.end (); ++task) + output.push_back (*task); + + for (task = completed.begin (); task != completed.end (); ++task) + output.push_back (*task); + } + + context.timer_filter.stop (); +} + +//////////////////////////////////////////////////////////////////////////////// +// If the filter contains the restriction "status:pending", as the first filter +// term, then completed.data does not need to be loaded. +bool Filter::shortcut () +{ +/* + // Postfix: <"pending"> <=> + // 0 1 2 + if (filter.size () >= 3 && + filter[0]._raw == "status" && + filter[1]._raw.find ("pending") != std::string::npos && + filter[2]._raw == "=") + { + context.debug ("Command::filter skipping completed.data (status:pending only)"); + return true; + } + + // Shortcut: If the filter contains no 'or' or 'xor' operators, IDs and no UUIDs. + int countId = 0; + int countUUID = 0; + int countOr = 0; + int countXor = 0; + std::vector ::const_iterator i; + for (i = filter.begin (); i != filter.end (); ++i) + { + if (i->_category == Arg::cat_op) + { + if (i->_raw == "or") ++countOr; + if (i->_raw == "xor") ++countXor; + + } + else if (i->_category == Arg::cat_id) ++countId; + else if (i->_category == Arg::cat_uuid) ++countUUID; + } + + if (countOr == 0 && + countXor == 0 && + countUUID == 0 && + countId > 0) + { + context.debug ("Command::filter skipping completed.data (IDs, no OR, no XOR, no UUID)"); + return true; + } +*/ + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// Disaster avoidance mechanism. +void Filter::safety () +{ +/* + if (! _read_only) + { + A3 write_filter = context.a3.extract_filter (); + if (!write_filter.size ()) // Potential disaster. + { + // If user is willing to be asked, this can be avoided. + if (context.config.getBoolean ("confirmation") && + confirm (STRING_TASK_SAFETY_VALVE)) + return; + + // No. + throw std::string (STRING_TASK_SAFETY_FAIL); + } + } +*/ +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/Filter.h b/src/Filter.h new file mode 100644 index 000000000..3d6d1dd94 --- /dev/null +++ b/src/Filter.h @@ -0,0 +1,50 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2006 - 2014, Paul Beckingham, Federico Hernandez. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// http://www.opensource.org/licenses/mit-license.php +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDED_FILTER +#define INCLUDED_FILTER + +#include +#include + +class Filter +{ +public: + Filter (); + ~Filter (); + + void subset (const std::vector &, std::vector &); + void subset (std::vector &); + bool shortcut (); + void safety (); + +private: + int _startCount; + int _endCount; +}; + +#endif +