raster pushed a commit to branch master. http://git.enlightenment.org/core/efl.git/commit/?id=bc4d43a1cca807737277e666f782699cb11b2111
commit bc4d43a1cca807737277e666f782699cb11b2111 Author: Carsten Haitzler (Rasterman) <ras...@rasterman.com> Date: Wed Apr 20 18:26:35 2016 +0900 eo base: names - add finding functions to fund by name this finds child objects by walking the child tree searching children by using the search string which can be name, class:name and both class and name can also be a glob. @feature --- src/lib/eo/eo_base.eo | 22 ++++- src/lib/eo/eo_base_class.c | 153 +++++++++++++++++++++++++++++++++++ src/tests/eo/suite/eo_test_general.c | 27 +++++++ 3 files changed, 198 insertions(+), 4 deletions(-) diff --git a/src/lib/eo/eo_base.eo b/src/lib/eo/eo_base.eo index 942abc0..8ac0573 100644 --- a/src/lib/eo/eo_base.eo +++ b/src/lib/eo/eo_base.eo @@ -77,10 +77,11 @@ abstract Eo.Base () [[ The id/name of the object. Every object can have a string name. Names may not contain - the slash "/" character. It is illegal. Using it in a name - will result in undefined behavior later on. An empty string - is considered the same as a NULL string or no string for the - name/id at all. + the following charactors: + / ? * [ ] ! \ : + They are illegal. Using it in a name will result in undefined + behavior later on. An empty string is considered the same as a + NULL string or no string for the name/id at all. ]] set { } @@ -152,6 +153,19 @@ abstract Eo.Base () [[Called at the end of #eo_add. Should not be called, just overridden.]] return: Eo.Base *; [[The new object created, can be NULL if aborting]] } + id_find { + [[Find a child object with the given name/id and return it. + The search string can be a glob (shell style). It can also + specify class name in the format of "class:name" where ":" + separates class and name. Both class and name can be globs. + If class is specified, and name is empty like "class:" then + the search will match any object of that class. + ]] + params { + @in search: const(char)*; [[the name/id search string]] + } + return: Eo.Base *; [[the first object found]] + } wref_add { [[Add a new weak reference to obj. diff --git a/src/lib/eo/eo_base_class.c b/src/lib/eo/eo_base_class.c index 2420568..07038e6 100644 --- a/src/lib/eo/eo_base_class.c +++ b/src/lib/eo/eo_base_class.c @@ -3,6 +3,7 @@ #endif #include <Eina.h> +#include <fnmatch.h> #define EO_BASE_BETA @@ -413,6 +414,158 @@ _eo_base_id_get(Eo *obj EINA_UNUSED, Eo_Base_Data *pd) return pd->extension->id; } +static inline Eina_Bool +_idmatch(const char *match, Eina_Bool is_glob, const char *str) +{ + if (str) + { + if (is_glob) + { + // if match string is empty - then it matches - same as "*" + if (!match[0]) return EINA_TRUE; + // if match string is "*" special case it and match + if ((match[0] == '*') && (match[1] == 0)) return EINA_TRUE; + // actual compare + if (!fnmatch(match, str, 0)) return EINA_TRUE; + } + else + { + // if match string is empty - then it matches - same as "*" + if (!match[0]) return EINA_TRUE; + // if pointers are the same they must be the same + if (match == str) return EINA_TRUE; + // actual compare + if (!strcmp(match, str)) return EINA_TRUE; + } + } + return EINA_FALSE; +} + +static inline Eina_Bool +_matchall(const char *match) +{ + if ((match[0] == 0) || ((match[0] == '*') && (match[1] == 0))) + return EINA_TRUE; + return EINA_FALSE; +} + +static Eina_Bool +_hasglob(const char *match) +{ + if (strpbrk(match, "*?[")) return EINA_TRUE; + return EINA_FALSE; +} + +static Eina_Bool +_ismultiglob(const char *match) +{ + if ((match[0] == '*') && (match[1] == '*') && (match[2] == 0)) + return EINA_TRUE; + if ((match[0] == '*') && (match[1] == '*') && (match[2] == '/')) + return EINA_TRUE; + if ((match[0] == '/') && (match[1] == '*') && (match[2] == '*') && (match[3] == 0)) + return EINA_TRUE; + if ((match[0] == '/') && (match[1] == '*') && (match[2] == '*') && (match[3] == '/')) + return EINA_TRUE; + return EINA_FALSE; +} + +EOLIAN static Eo_Base * +_eo_base_id_find(Eo *obj EINA_UNUSED, Eo_Base_Data *pd, const char *search) +{ + Eina_List *l; + Eo *child; + const char *id, *p, *klass_name; + size_t len; + + // notes: + // if search contains NO "/" char, then its just a name search. + // if there is one or more "/" chars, then these are explicitly object + // delimiters. + // a name of "**" means 0 or more objects in the heirachy chain + // if the string has no "/" char at the start, it implies "/**/" + // a name can be a name or the form "class:name" where the object must + // be of class named "class" and name "name". if "name" is empty like: + // "class:" then an object of any name will match like "class:*". an + // empty class like ":name" is the sanme as "*:name" which is the same + // as "name". class ane name of course can be basic globs but not ** + + // search string NULL or "" is invalid + if (!search) return NULL; + if (!search[0]) return NULL; + + len = strlen(search); + + if (strchr(search, '/')) + { + ERR("Looking up object by path '%s' is not supported", search); + return NULL; + } + else + { + // if this is a multi glob - "**" then we don't have a name or + // class to match at all so just don't look + if (_ismultiglob(search)) return NULL; + // check if this is "class:name" or just "name" + if ((p = strchr(search, ':'))) + { + // "class:name" + char *klass = alloca(len); + char *name = alloca(len); + Eina_Bool klass_glob = EINA_FALSE; + Eina_Bool name_glob = EINA_FALSE; + + // split class:name into 2 strings dropping : + strncpy(klass, search, p - search); + klass[p - search] = 0; + strcpy(name, p + 1); + + // figure out if class or name are globs + klass_glob = _hasglob(klass); + name_glob = _hasglob(name); + EINA_LIST_FOREACH(pd->children, l, child) + { + id = eo_id_get(child); + klass_name = eo_class_name_get(eo_class_get(child)); + if (_idmatch(klass, klass_glob, klass_name) && + (((!_matchall(klass)) && (!id) && (_matchall(name))) || + ((id) && _idmatch(name, name_glob, id)))) + return child; + child = eo_id_find(child, search); + if (child) return child; + } + } + else + { + if (_hasglob(search)) + { + // we have a glob - fnmatch + EINA_LIST_FOREACH(pd->children, l, child) + { + id = eo_id_get(child); + if ((id) && (_idmatch(search, EINA_TRUE, id))) + return child; + child = eo_id_find(child, search); + if (child) return child; + } + } + else + { + // fast path for simple "name" + EINA_LIST_FOREACH(pd->children, l, child) + { + id = eo_id_get(child); + if ((id) && (_idmatch(search, EINA_FALSE, id))) + return child; + child = eo_id_find(child, search); + if (child) return child; + } + } + } + } + return NULL; +} + EOLIAN static void _eo_base_comment_set(Eo *obj EINA_UNUSED, Eo_Base_Data *pd, const char *comment) { diff --git a/src/tests/eo/suite/eo_test_general.c b/src/tests/eo/suite/eo_test_general.c index c1ca0d8..44d39d2 100644 --- a/src/tests/eo/suite/eo_test_general.c +++ b/src/tests/eo/suite/eo_test_general.c @@ -1056,6 +1056,9 @@ START_TEST(eo_name) { eo_init(); Eo *obj = eo_add(SIMPLE_CLASS, NULL); + Eo *obj2 = eo_add(SIMPLE_CLASS, NULL); + Eo *obj3 = eo_add(SIMPLE_CLASS, NULL); + Eo *objtmp; const char *id; id = eo_id_get(obj); @@ -1076,6 +1079,30 @@ START_TEST(eo_name) id = eo_id_get(obj); fail_if(NULL != id); + eo_id_set(obj2, "joe"); + eo_id_set(obj3, "bob"); + + eo_parent_set(obj2, obj); + eo_parent_set(obj3, obj2); + + objtmp = eo_id_find(obj, "bob"); + fail_if(objtmp != obj3); + + objtmp = eo_id_find(obj, "joe"); + fail_if(objtmp != obj2); + + objtmp = eo_id_find(obj, "bo*"); + fail_if(objtmp != obj3); + + objtmp = eo_id_find(obj, "*oe"); + fail_if(objtmp != obj2); + + objtmp = eo_id_find(obj, "Simple:*oe"); + fail_if(objtmp != obj2); + + objtmp = eo_id_find(obj, "*mple:joe"); + fail_if(objtmp != obj2); + eo_del(obj); eo_shutdown(); --