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
This commit is contained in:
Carsten Haitzler 2016-04-20 18:26:35 +09:00
parent 5bec0d07b4
commit bc4d43a1cc
3 changed files with 198 additions and 4 deletions

View File

@ -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.

View File

@ -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)
{

View File

@ -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();