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

-- 


Reply via email to