From 0ec3b4b6af7b18f668471adff47689b45b3110ed Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Fri, 5 Jun 2009 01:49:53 -0400 Subject: [PATCH] Enhancement - Attribute modifiers - Implemented half the modifiers. The easy half. - Implemented unit tests that don't all pass yet, and are incomplete. --- src/Att.cpp | 96 +++++++++++++++++++++++++++++++------------- src/Filter.cpp | 12 +++++- src/tests/filt.t.cpp | 75 +++++++++++++++++++++++++++++++++- 3 files changed, 150 insertions(+), 33 deletions(-) diff --git a/src/Att.cpp b/src/Att.cpp index 1e6d223d9..65add817d 100644 --- a/src/Att.cpp +++ b/src/Att.cpp @@ -27,7 +27,8 @@ #include #include -#include +#include "text.h" +#include "util.h" #include "Att.h" //////////////////////////////////////////////////////////////////////////////// @@ -159,41 +160,78 @@ bool Att::validMod (const std::string& mod) const } //////////////////////////////////////////////////////////////////////////////// +// "this" is the attribute that has modifiers. "other" is the attribute from a +// Record that does not have modifiers, but may have a value. bool Att::match (const Att& other) const { - // No modifier means automatic pass. -/* - if (*this == "") // i18n: no - return true; -*/ + // Assume a match, and short-circuit on mismatch. + foreach (mod, mMods) + { + // is = equal. + if (*mod == "is") + if (mValue != other.mValue) + return false; - // TODO before - // TODO after - // TODO not - // TODO none - // TODO any - // TODO synth - // TODO under - // TODO over - // TODO first - // TODO last - // TODO this - // TODO next + // isnt = not equal. + if (*mod == "isnt") + if (mValue == other.mValue) + return false; -/* - if (*this == "is") // i18n: TODO - return *this == other ? true : false; + // any = any value, but not empty value. + if (*mod == "any") + if (other.mValue == "") + return false; - if (*this == "isnt") // i18n: TODO - return *this != other ? true : false; -*/ + // none = must have empty value. + if (*mod == "none") + if (other.mValue != "") + return false; - // TODO has - // TODO hasnt - // TODO startswith - // TODO endswith + // startswith = first characters must match. + if (*mod == "startswith") + { + if (other.mValue.length () < mValue.length ()) + return false; - return false; + if (mValue != other.mValue.substr (0, mValue.length ())) + return false; + } + + // endswith = last characters must match. + if (*mod == "endswith") + { + if (other.mValue.length () < mValue.length ()) + return false; + + if (mValue != other.mValue.substr ( + other.mValue.length () - mValue.length (), + std::string::npos)) + return false; + } + + // has = contains as a substring. + if (*mod == "has") + if (other.mValue.find (mValue) == std::string::npos) + return false; + + // hasnt = does not contain as a substring. + if (*mod == "hasnt") + if (other.mValue.find (mValue) != std::string::npos) + return false; + + // TODO before + // TODO after + // TODO not <-- could be a problem + // TODO synth + // TODO under + // TODO over + // TODO first + // TODO last + // TODO this + // TODO next + } + + return true; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/Filter.cpp b/src/Filter.cpp index 1ac864af0..cfa17634f 100644 --- a/src/Filter.cpp +++ b/src/Filter.cpp @@ -38,9 +38,17 @@ bool Filter::pass (const Record& record) const // If record doesn't have the attribute, fail. If it does have the attribute // but it doesn't match, fail. foreach (att, (*this)) - if ((r = record.find (att->name ())) == record.end () || - ! att->match (r->second)) + { + // If the record doesn't have the attribute, match against a default one. + // This is because "att" may contain a modifier like "name.not:X". + if ((r = record.find (att->name ())) == record.end ()) + { + if (! att->match (Att ())) return false; + } + else if (! att->match (r->second)) + return false; + } return true; } diff --git a/src/tests/filt.t.cpp b/src/tests/filt.t.cpp index 1560bdcb1..d7a035cbf 100644 --- a/src/tests/filt.t.cpp +++ b/src/tests/filt.t.cpp @@ -34,7 +34,7 @@ Context context; //////////////////////////////////////////////////////////////////////////////// int main (int argc, char** argv) { - UnitTest test (6); + UnitTest test (14); // Create a filter consisting of two Att criteria. Filter f; @@ -63,7 +63,78 @@ int main (int argc, char** argv) partial.set ("name1", "value1"); test.notok (f.pass (no0), "no match against partial T2"); - // TODO Modifiers. + // Modifiers. + T2 mods; + mods.set ("name", "value"); + + Att a ("name", "value"); + a.addMod ("is"); + f.clear (); + f.push_back (a); + test.ok (f.pass (mods), "name:value -> name.is:value = match"); + // TODO test inverse. + + a = Att ("name", "value"); + a.addMod ("isnt"); + f.clear (); + f.push_back (a); + test.notok (f.pass (mods), "name:value -> name.isnt:value = no match"); + // TODO test inverse. + + a = Att ("name", "val"); + a.addMod ("startswith"); + f.clear (); + f.push_back (a); + test.ok (f.pass (mods), "name:value -> name.startswith:val = match"); + // TODO test inverse. + + a = Att ("name", "lue"); + a.addMod ("endswith"); + f.clear (); + f.push_back (a); + test.ok (f.pass (mods), "name:value -> name.endswith:lue = match"); + // TODO test inverse. + + a = Att ("name", "value"); + a.addMod ("has"); + f.clear (); + f.push_back (a); + test.ok (f.pass (mods), "name:value -> name.has:alu = match"); + // TODO test inverse. + + a = Att ("name", "value"); + a.addMod ("hasnt"); + f.clear (); + f.push_back (a); + test.notok (f.pass (mods), "name:value -> name.hasnt:alu = no match"); + // TODO test inverse. + + a = Att ("name", ""); + a.addMod ("any"); + f.clear (); + f.push_back (a); + test.ok (f.pass (mods), "name:value -> name.any: = match"); + // TODO test inverse. + + a = Att ("name", ""); + a.addMod ("none"); + f.clear (); + f.push_back (a); + test.notok (f.pass (mods), "name:value -> name.none: = no match"); + // TODO test inverse. + +/* +"before" +"after" +"not" +"synth" +"under" +"over" +"first" +"last" +"this" +"next" +*/ return 0; }