[gthumb] utils: added more powerful functions to deal with templates



commit b22029edc6c50c004b6fac1a2546ca70b7c0a360
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Sun May 16 12:36:45 2021 +0200

    utils: added more powerful functions to deal with templates

 gthumb/str-utils.c       | 607 ++++++++++++++++++++++++++++++++++++++++-------
 gthumb/str-utils.h       |  44 +++-
 gthumb/test-glib-utils.c | 301 ++++++++++++++++++++++-
 3 files changed, 848 insertions(+), 104 deletions(-)
---
diff --git a/gthumb/str-utils.c b/gthumb/str-utils.c
index 828f6202..9b00107f 100644
--- a/gthumb/str-utils.c
+++ b/gthumb/str-utils.c
@@ -407,96 +407,6 @@ _g_utf8_split (const char *str,
 }
 
 
-/* -- _g_utf8_split_template -- */
-
-
-typedef enum {
-       SPLIT_TMPL_STATE_START,
-       SPLIT_TMPL_STATE_READING_SHARPS,
-       SPLIT_TMPL_STATE_READING_LITERAL
-} SplitTmplState;
-
-
-/**
- * example 1 : "xxx##yy#" --> [0] = xxx
- *                            [1] = ##
- *                            [2] = yy
- *                            [3] = #
- *                            [4] = NULL
- *
- * example 2 : ""         --> [0] = NULL
- **/
-char **
-_g_utf8_split_template (const char *tmpl)
-{
-       SplitTmplState   state;
-       GList           *chunk_list;
-       int              chunk_n;
-       const char      *p;
-       const char      *chunk_start;
-       const char      *chunk_end;
-       char           **chunk_v;
-
-       state = SPLIT_TMPL_STATE_START;
-       chunk_list = NULL;
-       chunk_n = 0;
-       p = tmpl;
-       while (p != NULL) {
-               gunichar ch = g_utf8_get_char (p);
-               gboolean save_chunk = FALSE;
-
-               switch (state) {
-               case SPLIT_TMPL_STATE_START:
-                       chunk_start = chunk_end = p;
-                       if (ch == '#')
-                               state = SPLIT_TMPL_STATE_READING_SHARPS;
-                       else
-                               state = SPLIT_TMPL_STATE_READING_LITERAL;
-                       break;
-
-               case SPLIT_TMPL_STATE_READING_SHARPS:
-                       if (ch != '#') {
-                               state = SPLIT_TMPL_STATE_READING_LITERAL;
-                               save_chunk = TRUE;
-                       }
-                       else
-                               chunk_end = p;
-                       break;
-
-               case SPLIT_TMPL_STATE_READING_LITERAL:
-                       if ((ch == '#') || (ch == 0)) {
-                               state = SPLIT_TMPL_STATE_READING_SHARPS;
-                               save_chunk = TRUE;
-                       }
-                       else
-                               chunk_end = p;
-                       break;
-               }
-
-               if (save_chunk) {
-                       glong  chunk_size;
-                       char  *chunk;
-
-                       chunk_size = chunk_end - chunk_start + 1;
-                       chunk = _g_utf8_strndup (chunk_start, chunk_size);
-                       chunk_list = g_list_prepend (chunk_list, chunk);
-                       chunk_n++;
-                       chunk_start = chunk_end = p;
-               }
-
-               if (ch == 0)
-                       break;
-
-               p = g_utf8_next_char (p);
-       }
-
-       chunk_v = _g_strv_take_from_str_list (chunk_list, chunk_n);
-       g_list_free (chunk_list);
-
-       return chunk_v;
-}
-
-
 char *
 _g_utf8_replace_str (const char *str,
                     const char *old_str,
@@ -1015,3 +925,520 @@ _g_utf8_remove_string_properties (const char *str)
 
        return (after_properties != NULL) ? g_strdup (after_properties) : NULL;
 }
+
+
+/* -- Template utils -- */
+
+
+#undef DEBUG_TOKENIZER
+
+
+typedef enum {
+       TOKENIZER_STATE_START,
+       TOKENIZER_STATE_ENUMERATOR,
+       TOKENIZER_STATE_CODE,
+       TOKENIZER_STATE_ARG,
+       TOKENIZER_STATE_TEXT
+} TokenizerState;
+
+
+#if DEBUG_TOKENIZER
+static char *TokenizerStateName[] = {
+       "TOKENIZER_STATE_START",
+       "TOKENIZER_STATE_ENUMERATOR",
+       "TOKENIZER_STATE_CODE",
+       "TOKENIZER_STATE_ARG",
+       "TOKENIZER_STATE_TEXT"
+};
+#endif
+
+
+static gboolean
+valid_char_after_code_prefix (gunichar ch)
+{
+       return (ch != '%') && (ch != '{') && ! g_unichar_isspace (ch) && (ch != 0);
+}
+
+
+char **
+_g_template_tokenize (const char    *template,
+                     TemplateFlags  flags)
+{
+       gboolean         split_enumerator;
+       TokenizerState   state;
+       GList           *token_list;
+       int              token_n;
+       const char      *p;
+       const char      *token_start;
+       glong            token_size;
+       int              n_open_brackets;
+       char           **token_v;
+
+#if DEBUG_TOKENIZER
+       g_print ("\n> _g_template_tokenize: '%s'\n", template);
+#endif
+
+       split_enumerator = (flags & TEMPLATE_FLAGS_NO_ENUMERATOR) == 0;
+
+       state = TOKENIZER_STATE_START;
+       token_list = NULL;
+       token_n = 0;
+       token_start = NULL;
+       token_size = 0;
+       n_open_brackets = 0;
+       p = template;
+       while (p != NULL) {
+               gboolean    add_to_token;
+               gboolean    save_token;
+               gunichar    ch;
+               const char *next_p;
+               gunichar    next_ch;
+               gboolean    code_prefix;
+               gboolean    enumerator_prefix;
+
+               add_to_token = FALSE;
+               save_token = FALSE;
+               ch = (p != NULL) ? g_utf8_get_char (p) : 0;
+               next_p = (p != NULL) ? g_utf8_next_char (p) : NULL;
+               next_ch = (next_p != NULL) ? g_utf8_get_char (next_p) : 0;
+               code_prefix = (ch == '%') && valid_char_after_code_prefix (next_ch);
+               enumerator_prefix = split_enumerator && (ch == '#');
+
+#if DEBUG_TOKENIZER
+               {
+                       char str[7] = { 0, 0, 0, 0, 0, 0, 0 };
+                       g_unichar_to_utf8 (ch, str);
+                       g_print ("> ch: '%s'\n", str);
+               }
+#endif
+
+               switch (state) {
+               case TOKENIZER_STATE_START:
+                       token_start = p;
+                       token_size = 0;
+                       add_to_token = TRUE;
+                       if (enumerator_prefix)
+                               state = TOKENIZER_STATE_ENUMERATOR;
+                       else if (code_prefix)
+                               state = TOKENIZER_STATE_CODE;
+                       else
+                               state = TOKENIZER_STATE_TEXT;
+                       break;
+
+               case TOKENIZER_STATE_ENUMERATOR:
+                       if (ch != '#') {
+                               add_to_token = FALSE;
+                               save_token = TRUE;
+                               state = TOKENIZER_STATE_START;
+                       }
+                       else
+                               add_to_token = TRUE;
+                       break;
+
+               case TOKENIZER_STATE_CODE:
+                       if (next_ch == '{') {
+                               add_to_token = TRUE;
+                               state = TOKENIZER_STATE_ARG;
+                               n_open_brackets = 0;
+                       }
+                       else {
+                               add_to_token = TRUE;
+                               save_token = TRUE;
+                               state = TOKENIZER_STATE_START;
+                       }
+                       break;
+
+               case TOKENIZER_STATE_ARG:
+                       if (ch == '{')
+                               n_open_brackets++;
+                       else if (ch == '}')
+                               n_open_brackets--;
+                       if ((n_open_brackets == 0) && (ch == '}') && (next_ch != '{')) {
+                               add_to_token = TRUE;
+                               save_token = TRUE;
+                               state = TOKENIZER_STATE_START;
+                       }
+                       else
+                               add_to_token = TRUE;
+                       break;
+
+               case TOKENIZER_STATE_TEXT:
+                       if ((ch == 0) || enumerator_prefix || code_prefix) {
+                               add_to_token = FALSE;
+                               save_token = TRUE;
+                               state = TOKENIZER_STATE_START;
+                       }
+                       else
+                               add_to_token = TRUE;
+                       break;
+               }
+
+#if DEBUG_TOKENIZER
+               g_print ("  new state: %s\n", TokenizerStateName[state]);
+               g_print ("  save_token: %d\n", save_token);
+               g_print ("  add_to_token: %d\n", add_to_token);
+               g_print ("  token_size: %ld\n", token_size);
+               g_print ("  n_open_brackets: %d\n", n_open_brackets);
+#endif
+
+               if (add_to_token) {
+                       token_size++;
+                       p = next_p;
+               }
+
+               if (save_token) {
+                       token_list = g_list_prepend (token_list, _g_utf8_strndup (token_start, token_size));
+                       token_n++;
+               }
+
+               if (ch == 0)
+                       break;
+       }
+
+       token_v = _g_strv_take_from_str_list (token_list, token_n);
+       g_list_free (token_list);
+
+       return token_v;
+}
+
+
+gunichar
+_g_template_get_token_code (const char *token)
+{
+       gunichar ch;
+
+       if (token == NULL)
+               return 0;
+
+       ch = g_utf8_get_char (token);
+       if (ch != '%')
+               return 0;
+
+       token = g_utf8_next_char (token);
+       if (token == NULL)
+               return 0;
+
+       ch = g_utf8_get_char (token);
+
+       return valid_char_after_code_prefix (ch) ? ch : 0;
+}
+
+
+char **
+_g_template_get_token_args (const char *token)
+{
+       TokenizerState   state;
+       GList           *token_list;
+       int              token_n;
+       const char      *p;
+       const char      *token_start;
+       glong            token_size;
+       int              n_open_brackets;
+       char           **token_v;
+
+#if DEBUG_TOKENIZER
+       g_print ("\n> _g_template_get_token_args: '%s'\n", token);
+#endif
+
+       state = TOKENIZER_STATE_START;
+       token_list = NULL;
+       token_n = 0;
+       token_start = NULL;
+       token_size = 0;
+       n_open_brackets = 0;
+       p = token;
+       while (p != NULL) {
+               gboolean    save_token = FALSE;
+               gunichar    ch;
+               const char *next_p;
+               gunichar    next_ch;
+
+               ch = (p != NULL) ? g_utf8_get_char (p) : 0;
+               next_p = (p != NULL) ? g_utf8_next_char (p) : NULL;
+               next_ch = (next_p != NULL) ? g_utf8_get_char (next_p) : 0;
+
+#if DEBUG_TOKENIZER
+               {
+                       char str[7] = { 0, 0, 0, 0, 0, 0, 0 };
+                       g_unichar_to_utf8 (ch, str);
+                       g_print ("> ch: '%s'\n", str);
+               }
+#endif
+
+               switch (state) {
+               case TOKENIZER_STATE_START:
+                       if ((ch == '%') && valid_char_after_code_prefix (next_ch)) {
+                               /* Skip the code. */
+                               p = next_p;
+                               next_p = (p != NULL) ? g_utf8_next_char (p) : NULL;
+                               state = TOKENIZER_STATE_CODE;
+                       }
+                       else
+                               ch = 0; /* Not a special code. */
+                       break;
+
+               case TOKENIZER_STATE_CODE:
+                       if (ch == '{') {
+                               n_open_brackets = 1;
+                               token_start = next_p;
+                               token_size = 0;
+                               state = TOKENIZER_STATE_ARG;
+                       }
+                       else
+                               ch = 0; /* No arguments for this code. */
+                       break;
+
+               case TOKENIZER_STATE_ARG:
+                       if (ch == '}') {
+                               n_open_brackets--;
+                               if (n_open_brackets == 0) {
+                                       save_token = TRUE;
+                                       state = TOKENIZER_STATE_CODE;
+                               }
+                               else
+                                       token_size++;
+                       }
+                       else {
+                               token_size++;
+                               if (ch == '{')
+                                       n_open_brackets++;
+                       }
+                       break;
+
+               default:
+                       g_assert_not_reached ();
+                       break;
+               }
+
+#if DEBUG_TOKENIZER
+               g_print ("  new state: %s\n", TokenizerStateName[state]);
+               g_print ("  save_token: %d\n", save_token);
+               g_print ("  token_size: %ld\n", token_size);
+               g_print ("  n_open_brackets: %d\n", n_open_brackets);
+#endif
+
+               if (save_token) {
+                       char *token;
+
+                       token = _g_utf8_strndup (token_start, token_size);
+                       token_list = g_list_prepend (token_list, _g_utf8_strip (token));
+                       token_n++;
+
+                       g_free (token);
+               }
+
+               if (ch == 0)
+                       break;
+
+               p = next_p;
+       }
+
+       token_v = _g_strv_take_from_str_list (token_list, token_n);
+       g_list_free (token_list);
+
+       return token_v;
+}
+
+
+gboolean
+_g_template_token_is (const char *token,
+                     gunichar    code)
+{
+       return _g_template_get_token_code (token) == code;
+}
+
+
+/* -- _g_template_for_each_token -- */
+
+
+static void
+_for_each_token (const char          *tmpl,
+                TemplateFlags        flags,
+                gunichar             parent_node,
+                TemplateForEachFunc  for_each,
+                gpointer             user_data)
+{
+       char     **tokenv;
+       int        i;
+       gboolean   with_enumerator;
+       gboolean   stop;
+
+       if ((tmpl == NULL) || (for_each == NULL))
+               return;
+
+       with_enumerator = (flags & TEMPLATE_FLAGS_NO_ENUMERATOR) == 0;
+       stop = FALSE;
+       tokenv = _g_template_tokenize (tmpl, flags);
+       for (i = 0; ! stop && (tokenv[i] != NULL); i++) {
+               char     *token = tokenv[i];
+               gunichar  code;
+
+               code = _g_template_get_token_code (token);
+               if (code != 0) {
+                       char **args;
+                       int    j;
+
+                       args = _g_template_get_token_args (token);
+                       for (j = 0; args[j] != NULL; j++)
+                               _for_each_token (args[j], flags, code, for_each, user_data);
+
+                       stop = for_each (parent_node, code, args, user_data);
+
+                       g_strfreev (args);
+               }
+               else if (with_enumerator && (token[0] == '#')) {
+                       char *args[] = { token, NULL };
+                       stop = for_each (parent_node, '#', args, user_data);
+               }
+               else {
+                       char *args[] = { token, NULL };
+                       stop = for_each (parent_node, code, args, user_data);
+               }
+       }
+
+       g_strfreev (tokenv);
+}
+
+
+void
+_g_template_for_each_token (const char          *tmpl,
+                           TemplateFlags        flags,
+                           TemplateForEachFunc  for_each,
+                           gpointer             user_data)
+{
+       _for_each_token (tmpl, flags, 0, for_each, user_data);
+}
+
+
+/* -- _g_template_eval -- */
+
+
+static gchar *
+_eval_template (const char       *tmpl,
+               TemplateFlags     flags,
+               gunichar          parent_code,
+               TemplateEvalFunc  eval,
+               gpointer          user_data)
+{
+       GString   *result;
+       char     **tokenv;
+       int        i;
+       gboolean   with_enumerator;
+       gboolean   stop;
+
+       if (tmpl == NULL)
+               return NULL;
+
+       result = g_string_new ("");
+       with_enumerator = (flags & TEMPLATE_FLAGS_NO_ENUMERATOR) == 0;
+       stop = FALSE;
+       tokenv = _g_template_tokenize (tmpl, flags);
+       for (i = 0; ! stop && (tokenv[i] != NULL); i++) {
+               char     *token = tokenv[i];
+               gunichar  code;
+
+               code = _g_template_get_token_code (token);
+               if (code != 0) {
+                       char **args;
+                       int    j;
+
+                       if (eval == NULL)
+                               continue;
+
+                       args = _g_template_get_token_args (token);
+                       for (j = 0; args[j] != NULL; j++)
+                               args[j] = _eval_template (args[j], flags, code, eval, user_data);
+
+                       stop = eval (flags, parent_code, code, args, result, user_data);
+
+                       g_strfreev (args);
+               }
+               else if (with_enumerator && (token[0] == '#')) {
+                       char *args[] = { token, NULL };
+
+                       if (eval == NULL)
+                               continue;
+
+                       stop = eval (flags, parent_code, '#', args, result, user_data);
+               }
+               else { /* Literal string. */
+                       if (flags & TEMPLATE_FLAGS_PREVIEW)
+                               _g_string_append_markup_escaped (result, "%s", token);
+                       else
+                               g_string_append (result, token);
+               }
+       }
+
+       g_strfreev (tokenv);
+
+       return g_string_free (result, FALSE);
+}
+
+
+char *
+_g_template_eval (const char       *tmpl,
+                 TemplateFlags     flags,
+                 TemplateEvalFunc  eval,
+                 gpointer          user_data)
+{
+       return _eval_template (tmpl,
+                              flags,
+                              0,
+                              eval,
+                              user_data);
+}
+
+
+void
+_g_string_append_template_code (GString   *str,
+                               gunichar   code,
+                               char     **args)
+{
+       if (code == 0) {
+               /* Literal string, not a special code. */
+               if (args[0] != NULL)
+                       g_string_append (str, args[0]);
+       }
+       else {
+               g_string_append_c (str, '%');
+               g_string_append_unichar (str, code);
+
+               if (args != NULL) {
+                       int i;
+
+                       for (i = 0; args[i] != NULL; i++)
+                               g_string_append_printf (str, "{ %s }", args[i]);
+               }
+       }
+}
+
+
+char *
+_g_template_replace_enumerator (const char *token,
+                               int         n)
+{
+       long  length;
+       char *format;
+       char *result;
+
+       length = 0;
+       while (token != NULL) {
+               gunichar ch = g_utf8_get_char (token);
+               if (ch == 0)
+                       break;
+               if (ch != '#')
+                       return NULL; /* Syntax error. */
+               length++;
+               token = g_utf8_next_char (token);
+       }
+
+       if (length == 0)
+               return NULL;
+
+       format = g_strdup_printf ("%%0" "%ld" "d", length);
+       result = g_strdup_printf (format, n);
+
+       g_free (format);
+
+       return result;
+}
diff --git a/gthumb/str-utils.h b/gthumb/str-utils.h
index cbd32656..950ad36d 100644
--- a/gthumb/str-utils.h
+++ b/gthumb/str-utils.h
@@ -67,7 +67,6 @@ const char *  _g_utf8_find_str        (const char      *haystack,
 char **                _g_utf8_split           (const char      *str,
                                         const char      *separator,
                                         int              max_tokens);
-char **                _g_utf8_split_template  (const char      *tmpl);
 char *         _g_utf8_replace_str     (const char      *str,
                                         const char      *old_str,
                                         const char      *new_str);
@@ -99,6 +98,49 @@ char *               _g_utf8_text_escape_xml (const char      *str);
 char *         _g_utf8_remove_string_properties
                                        (const char      *str);
 
+/* Template utils */
+
+typedef enum {
+       TEMPLATE_FLAGS_DEFAULT = 0,
+       TEMPLATE_FLAGS_NO_ENUMERATOR = (1 << 0),
+       TEMPLATE_FLAGS_PREVIEW = (1 << 1),
+       TEMPLATE_FLAGS_PARTIAL = (1 << 2),
+} TemplateFlags;
+
+typedef gboolean (*TemplateForEachFunc) (gunichar     parent_code,
+                                        gunichar     code,
+                                        char       **args,
+                                        gpointer     user_data);
+typedef gboolean (*TemplateEvalFunc)   (gunichar     root_code,
+                                        gunichar     parent_code,
+                                        gunichar     code,
+                                        char       **args,
+                                        GString     *result,
+                                        gpointer     user_data);
+typedef char *   (*TemplatePreviewFunc) (const char    *tmpl,
+                                        TemplateFlags  flags,
+                                        gpointer       user_data);
+
+char **        _g_template_tokenize            (const char           *tmpl,
+                                                TemplateFlags         flags);
+gunichar       _g_template_get_token_code      (const char           *token);
+char **        _g_template_get_token_args      (const char           *token);
+gboolean       _g_template_token_is            (const char           *token,
+                                                gunichar              code);
+void           _g_template_for_each_token      (const char           *tmpl,
+                                                TemplateFlags         flags,
+                                                TemplateForEachFunc   for_each,
+                                                gpointer              user_data);
+char *         _g_template_eval                (const char           *tmpl,
+                                                TemplateFlags         flags,
+                                                TemplateEvalFunc      eval,
+                                                gpointer              user_data);
+void           _g_string_append_template_code  (GString              *str,
+                                                gunichar              code,
+                                                char                **args);
+char *         _g_template_replace_enumerator  (const char           *token,
+                                                int                   n);
+
 G_END_DECLS
 
 #endif /* _STR_UTILS_H */
diff --git a/gthumb/test-glib-utils.c b/gthumb/test-glib-utils.c
index cf92f40c..f300ed04 100644
--- a/gthumb/test-glib-utils.c
+++ b/gthumb/test-glib-utils.c
@@ -525,53 +525,323 @@ test_g_utf8_split_all (void)
 
 
 static void
-test_g_utf8_split_template_all (void)
+test_g_template_tokenize_all (void)
 {
        char **strv;
 
-       strv = _g_utf8_split_template ("");
+       strv = _g_template_tokenize ("", 0);
        _g_assert_strv_equal (strv, NULL);
        g_strfreev (strv);
 
-       strv = _g_utf8_split_template ("xxx##yy#");
+       strv = _g_template_tokenize ("xxx##yy#", 0);
        _g_assert_strv_equal (strv, "xxx", "##", "yy", "#", NULL);
        g_strfreev (strv);
 
-       strv = _g_utf8_split_template ("日");
+       strv = _g_template_tokenize ("日", 0);
        _g_assert_strv_equal (strv, "日", NULL);
        g_strfreev (strv);
 
-       strv = _g_utf8_split_template ("日本");
+       strv = _g_template_tokenize ("日本", 0);
        _g_assert_strv_equal (strv, "日本", NULL);
        g_strfreev (strv);
 
-       strv = _g_utf8_split_template ("#");
+       strv = _g_template_tokenize ("#", 0);
        _g_assert_strv_equal (strv, "#", NULL);
        g_strfreev (strv);
 
-       strv = _g_utf8_split_template ("##");
+       strv = _g_template_tokenize ("##", 0);
        _g_assert_strv_equal (strv, "##", NULL);
        g_strfreev (strv);
 
-       strv = _g_utf8_split_template ("#日");
+       strv = _g_template_tokenize ("#日", 0);
        _g_assert_strv_equal (strv, "#", "日", NULL);
        g_strfreev (strv);
 
-       strv = _g_utf8_split_template ("日#");
+       strv = _g_template_tokenize ("日#", 0);
        _g_assert_strv_equal (strv, "日", "#", NULL);
        g_strfreev (strv);
 
-       strv = _g_utf8_split_template ("日#本");
+       strv = _g_template_tokenize ("日#本", 0);
        _g_assert_strv_equal (strv, "日", "#", "本", NULL);
        g_strfreev (strv);
 
-       strv = _g_utf8_split_template ("日#本#");
+       strv = _g_template_tokenize ("日#本#", 0);
        _g_assert_strv_equal (strv, "日", "#", "本", "#", NULL);
        g_strfreev (strv);
 
-       strv = _g_utf8_split_template ("日#本#語");
+       strv = _g_template_tokenize ("日#本#語", 0);
        _g_assert_strv_equal (strv, "日", "#", "本", "#", "語", NULL);
        g_strfreev (strv);
+
+       strv = _g_template_tokenize ("%日", 0);
+       _g_assert_strv_equal (strv, "%日", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_tokenize ("%日%本", 0);
+       _g_assert_strv_equal (strv, "%日", "%本", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_tokenize ("%日-%本", 0);
+       _g_assert_strv_equal (strv, "%日", "-", "%本", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_tokenize ("%日-%本-", 0);
+       _g_assert_strv_equal (strv, "%日", "-", "%本", "-", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_tokenize ("-%日-%本", 0);
+       _g_assert_strv_equal (strv, "-", "%日", "-", "%本", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_tokenize ("%日{ 本語 }", 0);
+       _g_assert_strv_equal (strv, "%日{ 本語 }", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_tokenize ("%日{ 本語 }日本語", 0);
+       _g_assert_strv_equal (strv, "%日{ 本語 }", "日本語", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_tokenize ("%日{ 本語 }%本", 0);
+       _g_assert_strv_equal (strv, "%日{ 本語 }", "%本", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_tokenize ("%日{ 本語 }%正{ 體字 }", 0);
+       _g_assert_strv_equal (strv, "%日{ 本語 }", "%正{ 體字 }", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_tokenize ("%日{ 本語 }%正{ 體字 }{ 本語 }", 0);
+       _g_assert_strv_equal (strv, "%日{ 本語 }", "%正{ 體字 }{ 本語 }", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_tokenize ("%日{ 本語 }-%正{ 體字 }{ 本語 }", 0);
+       _g_assert_strv_equal (strv, "%日{ 本語 }", "-", "%正{ 體字 }{ 本語 }", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_tokenize ("%日{ 本語 }-%日{ 本 }{ 語 }-", 0);
+       _g_assert_strv_equal (strv, "%日{ 本語 }", "-", "%日{ 本 }{ 語 }", "-", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_tokenize ("%Q{ %A{ Prompt: } }", 0);
+       _g_assert_strv_equal (strv, "%Q{ %A{ Prompt: } }", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_tokenize ("%Q{ { %A } }", 0);
+       _g_assert_strv_equal (strv, "%Q{ { %A } }", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_tokenize ("%Q{ {} }", 0);
+       _g_assert_strv_equal (strv, "%Q{ {} }", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_tokenize ("%Q{ { }", 0);
+       _g_assert_strv_equal (strv, NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_tokenize ("%Q{ %A{} }", 0);
+       _g_assert_strv_equal (strv, "%Q{ %A{} }", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_tokenize ("%Q{ %A{}{} }", 0);
+       _g_assert_strv_equal (strv, "%Q{ %A{}{} }", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_tokenize ("%Q{ %A{ Prompt: }{ 12 } }", 0);
+       _g_assert_strv_equal (strv, "%Q{ %A{ Prompt: }{ 12 } }", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_tokenize ("%a{ %x }%b{ %y }", 0);
+       _g_assert_strv_equal (strv, "%a{ %x }", "%b{ %y }", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_tokenize ("%a{ %x }%b{ %y }%c{ %z }", 0);
+       _g_assert_strv_equal (strv, "%a{ %x }", "%b{ %y }", "%c{ %z }", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_tokenize ("%a{%x}%b{%y}%c{%z}", 0);
+       _g_assert_strv_equal (strv, "%a{%x}", "%b{%y}", "%c{%z}", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_tokenize ("%a{%x} %b{%y} %c{%z}", 0);
+       _g_assert_strv_equal (strv, "%a{%x}", " ", "%b{%y}", " ", "%c{%z}", NULL);
+       g_strfreev (strv);
+}
+
+
+static void
+test_g_template_get_token_code_all (void)
+{
+       g_assert_true (_g_template_get_token_code (NULL) == 0);
+       g_assert_true (_g_template_get_token_code ("") == 0);
+       g_assert_true (_g_template_get_token_code ("a") == 0);
+       g_assert_true (_g_template_get_token_code ("日") == 0);
+       g_assert_true (_g_template_get_token_code ("%a") == 'a');
+       g_assert_true (_g_template_get_token_code ("% a") == 0);
+       g_assert_true (_g_template_get_token_code ("%日") == g_utf8_get_char ("日"));
+       g_assert_true (_g_template_get_token_code ("%日 %字") == g_utf8_get_char ("日"));
+       g_assert_true (_g_template_get_token_code ("% 日") == 0);
+       g_assert_true (_g_template_get_token_code (" %日") == 0);
+       g_assert_true (_g_template_get_token_code (" % 日") == 0);
+       g_assert_true (_g_template_get_token_code ("%{}") == 0);
+       g_assert_true (_g_template_get_token_code ("%{ text }") == 0);
+       g_assert_true (_g_template_get_token_code ("%{ %字 }") == 0);
+       g_assert_true (_g_template_get_token_code ("%日{}") == g_utf8_get_char ("日"));
+       g_assert_true (_g_template_get_token_code ("%日{%字}") == g_utf8_get_char ("日"));
+       g_assert_true (_g_template_get_token_code ("%日{ }") == g_utf8_get_char ("日"));
+       g_assert_true (_g_template_get_token_code ("%日{ %字 }") == g_utf8_get_char ("日"));
+}
+
+
+static void
+test_g_template_get_token_args_all (void)
+{
+       char **strv;
+
+       strv = _g_template_get_token_args ("");
+       _g_assert_strv_equal (strv, NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_get_token_args ("%F");
+       _g_assert_strv_equal (strv, NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_get_token_args ("{ xxx }{ yyy }");
+       _g_assert_strv_equal (strv, NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_get_token_args ("%A%F{ xxx }{ yyy }");
+       _g_assert_strv_equal (strv, NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_get_token_args ("%F{ xxx }{ yyy }");
+       _g_assert_strv_equal (strv, "xxx", "yyy", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_get_token_args ("%日{ 本語 }");
+       _g_assert_strv_equal (strv, "本語", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_get_token_args ("%日{ 本語 }{ 體字 }");
+       _g_assert_strv_equal (strv, "本語", "體字", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_get_token_args ("%Q{ %A }");
+       _g_assert_strv_equal (strv, "%A", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_get_token_args ("%Q{ %A{ Prompt: } }");
+       _g_assert_strv_equal (strv, "%A{ Prompt: }", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_get_token_args ("%Q{ %A{ Prompt: }{ 10 } }");
+       _g_assert_strv_equal (strv, "%A{ Prompt: }{ 10 }", NULL);
+       g_strfreev (strv);
+
+       strv = _g_template_get_token_args ("%Q{ %A{ Prompt: }{ 10 } %A{ Prompt2: }{ 20 } }");
+       _g_assert_strv_equal (strv, "%A{ Prompt: }{ 10 } %A{ Prompt2: }{ 20 }", NULL);
+       g_strfreev (strv);
+}
+
+
+static void
+test_g_template_token_is_all (void)
+{
+       g_assert_true (_g_template_token_is (NULL, 0));
+       g_assert_true (_g_template_token_is ("", 0));
+
+       g_assert_true (_g_template_token_is ("a", 0));
+       g_assert_true (! _g_template_token_is ("a", 'a'));
+       g_assert_true (! _g_template_token_is ("a", 'b'));
+
+       g_assert_true (! _g_template_token_is ("%a", 0));
+       g_assert_true (_g_template_token_is ("%a", 'a'));
+       g_assert_true (_g_template_token_is ("%abc", 'a'));
+       g_assert_true (_g_template_token_is ("%a{}", 'a'));
+       g_assert_true (_g_template_token_is ("%a{ xyz }", 'a'));
+
+       g_assert_true (_g_template_token_is ("%日", g_utf8_get_char ("日")));
+       g_assert_true (_g_template_token_is ("%日{}", g_utf8_get_char ("日")));
+       g_assert_true (_g_template_token_is ("%日{ 日本語 }", g_utf8_get_char ("日")));
+}
+
+
+static gboolean
+eval_template_cb (gunichar     root_code,
+                 gunichar     parent_code,
+                 gunichar     code,
+                 char       **args,
+                 GString     *result,
+                 gpointer     user_data)
+{
+       int i;
+
+       switch (code) {
+       case 'A':
+               g_string_append (result, "a");
+               break;
+       case 'B':
+               for (i = 0; args[i] != NULL; i++)
+                       g_string_append (result, args[i]);
+               break;
+       case 'C':
+               break;
+       case '#':
+               g_string_append (result, _g_template_replace_enumerator (args[0], 1));
+               break;
+       default:
+               g_string_append_unichar (result, code);
+               break;
+       }
+       return FALSE;
+}
+
+
+static void
+test_g_template_eval_all (void)
+{
+       g_assert_cmpstr (_g_template_eval (NULL, 0, eval_template_cb, NULL), ==, NULL);
+       g_assert_cmpstr (_g_template_eval ("", 0, eval_template_cb, NULL), ==, "");
+       g_assert_cmpstr (_g_template_eval ("日本語", 0, eval_template_cb, NULL), ==, "日本語");
+       g_assert_cmpstr (_g_template_eval ("%A", 0, eval_template_cb, NULL), ==, "a");
+       g_assert_cmpstr (_g_template_eval ("%A{}", 0, eval_template_cb, NULL), ==, "a");
+       g_assert_cmpstr (_g_template_eval ("%A{ xyz }", 0, eval_template_cb, NULL), ==, "a");
+       g_assert_cmpstr (_g_template_eval ("%A{ %B }", 0, eval_template_cb, NULL), ==, "a");
+       g_assert_cmpstr (_g_template_eval ("%A{ %A }", 0, eval_template_cb, NULL), ==, "a");
+       g_assert_cmpstr (_g_template_eval ("%B{x}", 0, eval_template_cb, NULL), ==, "x");
+       g_assert_cmpstr (_g_template_eval ("%B{%B{x}}", 0, eval_template_cb, NULL), ==, "x");
+       g_assert_cmpstr (_g_template_eval ("%B{%B{x}y}z", 0, eval_template_cb, NULL), ==, "xyz");
+       g_assert_cmpstr (_g_template_eval ("%B{%B{x}%B{y}}", 0, eval_template_cb, NULL), ==, "xy");
+       g_assert_cmpstr (_g_template_eval ("%A%B%C", 0, eval_template_cb, NULL), ==, "a");
+       g_assert_cmpstr (_g_template_eval ("%A{}%B{ x }%C{ xyz }", 0, eval_template_cb, NULL), ==, "ax");
+       g_assert_cmpstr (_g_template_eval ("%A%B%C日本語", 0, eval_template_cb, NULL), ==, "a日本語");
+       g_assert_cmpstr (_g_template_eval ("日本語%A%B{日}%C", 0, eval_template_cb, NULL), ==, "日本語a日");
+
+       g_assert_cmpstr (_g_template_eval ("#", 0, eval_template_cb, NULL), ==, "1");
+       g_assert_cmpstr (_g_template_eval ("##", 0, eval_template_cb, NULL), ==, "01");
+       g_assert_cmpstr (_g_template_eval ("日##", 0, eval_template_cb, NULL), ==, "日01");
+       g_assert_cmpstr (_g_template_eval ("日#本##語###", 0, eval_template_cb, NULL), ==, "日1本01語001");
+       g_assert_cmpstr (_g_template_eval ("#日##本###語", 0, eval_template_cb, NULL), ==, "1日01本001語");
+       g_assert_cmpstr (_g_template_eval ("%A#%B#%C##", 0, eval_template_cb, NULL), ==, "a1101");
+       g_assert_cmpstr (_g_template_eval ("日本語%A%B{日}%C###", 0, eval_template_cb, NULL), ==, "日本語a日001");
+
+       g_assert_cmpstr (_g_template_eval ("#", TEMPLATE_FLAGS_NO_ENUMERATOR, eval_template_cb, NULL), ==, 
"#");
+       g_assert_cmpstr (_g_template_eval ("%A#%B{#}%C##", TEMPLATE_FLAGS_NO_ENUMERATOR, eval_template_cb, 
NULL), ==, "a####");
+}
+
+
+static void
+test_g_template_replace_enumerator_all (void)
+{
+       g_assert_cmpstr (_g_template_replace_enumerator (NULL, 1), ==, NULL);
+       g_assert_cmpstr (_g_template_replace_enumerator ("", 1), ==, NULL);
+       g_assert_cmpstr (_g_template_replace_enumerator ("X", 1), ==, NULL);
+       g_assert_cmpstr (_g_template_replace_enumerator ("日", 1), ==, NULL);
+       g_assert_cmpstr (_g_template_replace_enumerator ("日#", 1), ==, NULL);
+       g_assert_cmpstr (_g_template_replace_enumerator ("#日", 1), ==, NULL);
+       g_assert_cmpstr (_g_template_replace_enumerator ("#", 1), ==, "1");
+       g_assert_cmpstr (_g_template_replace_enumerator ("##", 1), ==, "01");
+       g_assert_cmpstr (_g_template_replace_enumerator ("###", 1), ==, "001");
+       g_assert_cmpstr (_g_template_replace_enumerator ("###", 10), ==, "010");
+       g_assert_cmpstr (_g_template_replace_enumerator ("###", 1000), ==, "1000");
 }
 
 
@@ -931,7 +1201,12 @@ main (int   argc,
        g_test_add_func ("/glib-utils/_g_utf8_replace_str", test_g_utf8_replace_str_all);
        g_test_add_func ("/glib-utils/_g_utf8_rstrip", test_g_utf8_rstrip_all);
        g_test_add_func ("/glib-utils/_g_utf8_split", test_g_utf8_split_all);
-       g_test_add_func ("/glib-utils/_g_utf8_split_template", test_g_utf8_split_template_all);
+       g_test_add_func ("/glib-utils/_g_template_tokenize", test_g_template_tokenize_all);
+       g_test_add_func ("/glib-utils/_g_template_get_token_code", test_g_template_get_token_code_all);
+       g_test_add_func ("/glib-utils/_g_template_get_token_args", test_g_template_get_token_args_all);
+       g_test_add_func ("/glib-utils/_g_template_token_is", test_g_template_token_is_all);
+       g_test_add_func ("/glib-utils/_g_template_eval", test_g_template_eval_all);
+       g_test_add_func ("/glib-utils/_g_template_replace_enumerator", 
test_g_template_replace_enumerator_all);
        g_test_add_func ("/glib-utils/_g_utf8_strip", test_g_utf8_strip_all);
        g_test_add_func ("/glib-utils/_g_utf8_translate", test_g_utf8_translate_all);
        g_test_add_func ("/glib-utils/_g_utf8_remove_string_properties", 
test_g_utf8_remove_string_properties);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]