From 60d1c87cef43311a122bc570b35447d8adcfd679 Mon Sep 17 00:00:00 2001 From: James Turner Date: Thu, 13 Apr 2017 07:29:05 +0100 Subject: [PATCH] New string helper function: property path matching Match property path strings against template strings containing wild card characters. --- simgear/misc/strutils.cxx | 75 ++++++++++++++++++++++++++++++++++ simgear/misc/strutils.hxx | 14 +++++++ simgear/misc/strutils_test.cxx | 27 ++++++++++++ 3 files changed, 116 insertions(+) diff --git a/simgear/misc/strutils.cxx b/simgear/misc/strutils.cxx index a9d76e24..405a980c 100644 --- a/simgear/misc/strutils.cxx +++ b/simgear/misc/strutils.cxx @@ -754,6 +754,81 @@ bool to_bool(const std::string& s) return false; } +enum PropMatchState +{ + MATCH_LITERAL = 0, + MATCH_WILD_INDEX, + MATCH_WILD_NAME +}; + +bool matchPropPathToTemplate(const std::string& path, const std::string& templatePath) +{ + if (path.empty()) { + return false; + } + + const char* pathPtr = path.c_str(); + const char* tPtr = templatePath.c_str(); + PropMatchState state = MATCH_LITERAL; + + while (true) { + bool advanceInTemplate = true; + const char p = *pathPtr; + if (p == 0) { + // ran out of chars in the path. If we are matching a trailing + // wildcard, this is a match, otherwise it's a fail + if (state == MATCH_WILD_NAME) { + // check this is the last * in the template string + if (*(tPtr + 1) == 0) { + return true; + } + } + + return false; + } + + switch (state) { + case MATCH_LITERAL: + if (*tPtr != p) { + // literal mismatch + return false; + } + ++pathPtr; + break; + case MATCH_WILD_NAME: + if ((p == '-') || isalpha(p)) { + advanceInTemplate = false; + ++pathPtr; + } else { + // something else, we will advance in the template + } + break; + case MATCH_WILD_INDEX: + if (isdigit(p)) { + advanceInTemplate = false; + ++pathPtr; + } else { + // something else, we will advance in the template + } + break; + } // of state switch + + if (advanceInTemplate) { + const char nextTemplate = *(++tPtr); + if (nextTemplate == 0) { + // end of template, successful match + return true; + } else if (nextTemplate == '*') { + state = (*(tPtr - 1) == '[') ? MATCH_WILD_INDEX : MATCH_WILD_NAME; + } else { + state = MATCH_LITERAL; + } + } + } + + // unreachable +} + } // end namespace strutils } // end namespace simgear diff --git a/simgear/misc/strutils.hxx b/simgear/misc/strutils.hxx index 3844e7f9..e49d67e3 100644 --- a/simgear/misc/strutils.hxx +++ b/simgear/misc/strutils.hxx @@ -265,6 +265,20 @@ namespace simgear { */ std::string error_string(int errnum); + + /** + * Match a property path, obtained from prop->getPath(), against a + * template string. Templates are allowed to contain widlcards denoted by + * an asterix in certain places - at the end of names, or inside indices. + * Note that paths returned by getPath() always include an index on every + * path component, so template strings should be structured accordingly. + * + * Examples: + * /foo[*]/bar* will match /foo/barber, /foo[2]/bargain + * /views[0]/view[*]/f* will match /views[0]/view[99]/foo, + * /views[0]/view[4]/fig, /views[0]/view[1000]/flight + */ + bool matchPropPathToTemplate(const std::string& path, const std::string& templatePath); } // end namespace strutils } // end namespace simgear diff --git a/simgear/misc/strutils_test.cxx b/simgear/misc/strutils_test.cxx index 3d2ebdb5..073d474c 100644 --- a/simgear/misc/strutils_test.cxx +++ b/simgear/misc/strutils_test.cxx @@ -175,6 +175,32 @@ void test_md5_hex() SG_CHECK_EQUAL(strutils::md5("test"), "098f6bcd4621d373cade4e832627b4f6"); } +void test_propPathMatch() +{ + const char* testTemplate1 = "/sim[*]/views[*]/render"; + SG_VERIFY(strutils::matchPropPathToTemplate("/sim[0]/views[50]/render-buildings[0]", testTemplate1)); + SG_VERIFY(strutils::matchPropPathToTemplate("/sim[1]/views[0]/rendering-enabled", testTemplate1)); + + SG_VERIFY(!strutils::matchPropPathToTemplate("/sim[0]/views[50]/something-else", testTemplate1)); + SG_VERIFY(!strutils::matchPropPathToTemplate("/sim[0]/gui[0]/wibble", testTemplate1)); + + // test explicit index matching + const char* testTemplate2 = "/view[5]/*"; + SG_VERIFY(!strutils::matchPropPathToTemplate("/view[2]/render-buildings[0]", testTemplate2)); + SG_VERIFY(!strutils::matchPropPathToTemplate("/sim[1]/foo", testTemplate2)); + SG_VERIFY(!strutils::matchPropPathToTemplate("/view[50]/foo", testTemplate2)); + SG_VERIFY(!strutils::matchPropPathToTemplate("/view[55]/foo", testTemplate2)); + + SG_VERIFY(strutils::matchPropPathToTemplate("/view[5]/foo", testTemplate2)); + SG_VERIFY(strutils::matchPropPathToTemplate("/view[5]/child[3]/bar", testTemplate2)); + + + const char* testTemplate3 = "/*[*]/fdm*[*]/aero*"; + + SG_VERIFY(strutils::matchPropPathToTemplate("/position[2]/fdm-jsb[0]/aerodynamic", testTemplate3)); + SG_VERIFY(!strutils::matchPropPathToTemplate("/position[2]/foo[0]/aerodynamic", testTemplate3)); +} + void test_error_string() { #if defined(_WIN32) @@ -210,6 +236,7 @@ int main(int argc, char* argv[]) test_compare_versions(); test_md5_hex(); test_error_string(); + test_propPathMatch(); return EXIT_SUCCESS; }