diff --git a/apteryx-xml.h b/apteryx-xml.h
index 6d472a5..3cfc66d 100644
--- a/apteryx-xml.h
+++ b/apteryx-xml.h
@@ -93,6 +93,9 @@ char *sch_translate_from (sch_node * node, char *value);
bool sch_validate_pattern (sch_node * node, const char *value);
gboolean sch_match_name (const char *s1, const char *s2);
bool sch_ns_match (sch_node *node, void *ns);
+GNode *sch_translate_input (sch_instance * instance, GNode *node, int flags,
+ void **xlat_data, sch_node **rschema);
+GNode *sch_translate_output (sch_instance * instance, GNode *node, int flags, void *xlat_data);
/* Data translation/manipulation */
typedef enum
diff --git a/models/xlat_test.lua b/models/xlat_test.lua
new file mode 100644
index 0000000..9c6c619
--- /dev/null
+++ b/models/xlat_test.lua
@@ -0,0 +1,29 @@
+xlat_data = {}
+
+xlat_data.translate_type = function(field, value, dir)
+ local value1 = value
+ if field == 'type' then
+ value1 = tonumber (value)
+ print("field " .. field)
+ print("value1 " .. value1)
+ print("dir " .. dir)
+ if dir == 'out' then
+ if value == '1' then
+ value1 = '3'
+ else
+ value1 = '4'
+ end
+ print("value1 " .. value1)
+ else
+ if value == '3' then
+ value1 = '1'
+ else
+ value1 = '2'
+ end
+ end
+ end
+ return value1
+end
+
+return xlat_data
+
diff --git a/models/xlat_test.xlat b/models/xlat_test.xlat
new file mode 100644
index 0000000..e4f297e
--- /dev/null
+++ b/models/xlat_test.xlat
@@ -0,0 +1,7 @@
+/xlat-test/xlat-animals /test/animals/animal
+/xlat-test/xlat-animals/xlat-animal /test/animals/animal
+/xlat-test/xlat-animals/xlat-animal/*/name /test/animals/animal/*/name
+/xlat-test/xlat-animals/xlat-animal/*/type /test/animals/animal/*/type xlat_data.translate_type
+/xlat-test/xlat-animals/xlat-animal/*/colour /test/animals/animal/*/colour
+/xlat-test/xlat-animals/xlat-animal/*/food /test/animals/animal/*/food
+/xlat-test/xlat-animals/xlat-animal/*/toys /test/animals/animal/*/toys
\ No newline at end of file
diff --git a/models/xlat_test.xml b/models/xlat_test.xml
new file mode 100644
index 0000000..9c61ab3
--- /dev/null
+++ b/models/xlat_test.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/schema.c b/schema.c
index 89452c5..ba74306 100644
--- a/schema.c
+++ b/schema.c
@@ -32,6 +32,9 @@
#include
#include
#include
+#include
+#include
+#include
#include "apteryx-xml.h"
/* Error handling and debug */
@@ -60,7 +63,10 @@ typedef struct _sch_instance
GList *models_list;
GHashTable *map_hash_table;
GHashTable *model_hash_table;
+ GHashTable *xlat_hash_table;
GList *regexes;
+ /* Lua state */
+ lua_State *ls;
} sch_instance;
typedef struct _sch_xml_to_gnode_parms_s
@@ -77,6 +83,20 @@ typedef struct _sch_xml_to_gnode_parms_s
GList *out_replaces;
} _sch_xml_to_gnode_parms;
+typedef struct _sch_xlat_root
+{
+ GList *matchs;
+} sch_xlat_root;
+
+typedef struct _sch_xlat_data
+{
+ sch_xlat_root *root;
+ GNode *ext_tree;
+ GNode *int_tree;
+ char *lua_module;
+ char *lua_func;
+} sch_xlat_data;
+
/* Retrieve the last error code */
sch_err
sch_last_err (void)
@@ -91,6 +111,49 @@ sch_last_errmsg (void)
return tl_errmsg;
}
+static char *
+first_name_in_path (const char *path)
+{
+ const char *in = path;
+ char *slash;
+ char *name;
+
+ if (!in || in[0] == '\0')
+ return NULL;
+
+ name = strdup (in);
+ slash = strchr (name + 1, '/');
+ if (slash)
+ *slash = '\0';
+ return name;
+}
+
+static void
+sch_lua_error (lua_State *ls, int res)
+{
+ switch (res)
+ {
+ case LUA_ERRRUN:
+ syslog (LOG_ERR, "LUA: %s\n", lua_tostring (ls, -1));
+ break;
+ case LUA_ERRSYNTAX:
+ syslog (LOG_ERR, "LUA: %s\n", lua_tostring (ls, -1));
+ break;
+ case LUA_ERRMEM:
+ syslog (LOG_ERR, "LUA: Memory allocation error\n");
+ break;
+ case LUA_ERRERR:
+ syslog (LOG_ERR, "LUA: Error handler error\n");
+ break;
+ case LUA_ERRFILE:
+ syslog (LOG_ERR, "LUA: Couldn't open file\n");
+ break;
+ default:
+ syslog (LOG_ERR, "LUA: Unknown error\n");
+ break;
+ }
+}
+
/* List full paths for all schema files in the search path */
static void
list_schema_files (GList ** files, const char *path)
@@ -113,7 +176,8 @@ list_schema_files (GList ** files, const char *path)
char *filename;
if ((fnmatch ("*.xml", ep->d_name, FNM_PATHNAME) != 0) &&
(fnmatch ("*.xml.gz", ep->d_name, FNM_PATHNAME) != 0) &&
- (fnmatch ("*.map", ep->d_name, FNM_PATHNAME) != 0))
+ (fnmatch ("*.map", ep->d_name, FNM_PATHNAME) != 0) &&
+ (fnmatch ("*.xlat", ep->d_name, FNM_PATHNAME) != 0))
{
continue;
}
@@ -551,6 +615,136 @@ sch_load_namespace_mappings (sch_instance *instance, const char *filename)
g_free (buf);
}
+GNode *
+path_to_tree (char *path)
+{
+ GNode *ret_node = NULL;
+ GNode *node = NULL;;
+ gchar **path_split;
+ int i;
+ int split_len;
+
+ path_split = g_strsplit (path, "/", -1);
+ split_len = g_strv_length (path_split);
+ for (i = 0; i < split_len; i++)
+ {
+ if (i == 0 && strlen (path_split[0]) == 0)
+ {
+ if (split_len > 1)
+ {
+ char *tmp = path_split[1];
+ path_split[1] = g_strdup_printf ("/%s", tmp);
+ g_free (tmp);
+ }
+ continue;
+ }
+ if (!node)
+ {
+ node = g_node_new (g_strdup (path_split[i]));
+ ret_node = node;
+ }
+ else
+ node = g_node_append_data (node, g_strdup (path_split[i]));
+ }
+ g_strfreev (path_split);
+
+ return ret_node;
+}
+
+static void
+free_xlat_root (gpointer data)
+{
+ sch_xlat_root *root = (sch_xlat_root *) data;
+ sch_xlat_data *xlat_data;
+ GList *list;
+
+ for (list = g_list_first (root->matchs); list; list = g_list_next (list))
+ {
+ xlat_data = list->data;
+ apteryx_free_tree (xlat_data->ext_tree);
+ apteryx_free_tree (xlat_data->int_tree);
+ g_free (xlat_data->lua_module);
+ g_free (xlat_data->lua_func);
+ g_free (xlat_data);
+ list->data = NULL;
+ }
+ g_list_free (root->matchs);
+ g_free (root);
+}
+
+static void
+sch_load_model_xlat (sch_instance *instance, const char *filename)
+{
+ FILE *fp = NULL;
+ gchar **xlat_paths;
+ char *buf;
+ char *name;
+ sch_xlat_root *root;
+ sch_xlat_data *xlat_data;
+
+ if (!instance)
+ return;
+
+ buf = g_malloc0 (READ_BUF_SIZE);
+ fp = fopen (filename, "r");
+ if (fp && buf)
+ {
+ if (!instance->xlat_hash_table)
+ instance->xlat_hash_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_xlat_root);
+
+ while (fgets (buf, READ_BUF_SIZE, fp) != NULL)
+ {
+ /* Skip comment lines */
+ if (buf[0] == '#')
+ continue;
+
+ /* Remove any trailing LF */
+ buf[strcspn(buf, "\n")] = '\0';
+ /* xlat file format: request_path translate_path */
+ xlat_paths = g_strsplit (buf, " ", 3);
+ if (xlat_paths[0] && xlat_paths[1])
+ {
+ void *old_key;
+
+ name = first_name_in_path (xlat_paths[0]);
+ xlat_data = g_malloc0 (sizeof (sch_xlat_data));
+ xlat_data->ext_tree = path_to_tree (xlat_paths[0]);
+ xlat_data->int_tree = path_to_tree (xlat_paths[1]);
+ if (xlat_paths[2])
+ {
+ gchar **func_parts = g_strsplit (xlat_paths[2], ".", 2);
+ if (func_parts[0] && func_parts[1])
+ {
+ xlat_data->lua_module = g_strdup (func_parts[0]);
+ xlat_data->lua_func = g_strdup (func_parts[1]);
+ }
+ g_strfreev (func_parts);
+ }
+
+ /* Look up this node name to check for duplicates. */
+ if (g_hash_table_lookup_extended (instance->xlat_hash_table, name,
+ &old_key, (gpointer *) &root))
+ {
+ /* insert data into GList paths */
+ xlat_data->root = root;
+ root->matchs = g_list_prepend (root->matchs, xlat_data);
+ g_free (name);
+ }
+ else
+ {
+ root = g_malloc0 (sizeof (sch_xlat_root));
+ xlat_data->root = root;
+ root->matchs = g_list_prepend (root->matchs, xlat_data);
+ g_hash_table_insert (instance->xlat_hash_table, name, root);
+ }
+ }
+ g_strfreev (xlat_paths);
+ }
+ fclose (fp);
+ }
+ g_free (buf);
+}
+
static void
sch_load_model_list (sch_instance *instance, const char *path, const char *model_list_filename)
{
@@ -602,6 +796,69 @@ sch_load_model_list (sch_instance *instance, const char *path, const char *model
g_free (buf);
}
+static bool
+sch_load_script_files (sch_instance *instance, const char *path)
+{
+ struct dirent *entry;
+ DIR *dir;
+ bool res = true;
+
+ /* Initialise the Lua state */
+ instance->ls = luaL_newstate ();
+ if (!instance->ls)
+ {
+ syslog (LOG_ERR, "XML: Failed to instantiate Lua interpreter\n");
+ return false;
+ }
+
+ /* Load required libraries */
+ luaL_openlibs (instance->ls);
+
+ /* Find all the LUA files in this folder */
+ dir = opendir (path);
+ if (dir == NULL)
+ return true;
+
+ /* Load and execute all LUA files */
+ for (entry = readdir (dir); entry; entry = readdir (dir))
+ {
+ const char *ext = strrchr (entry->d_name, '.');
+ if (ext && strcmp (".lua", ext) == 0)
+ {
+ char *filename = g_strdup_printf ("%s/%s", path, entry->d_name);
+ int error;
+
+ /* Execute the script */
+ lua_getglobal (instance->ls, "debug");
+ lua_getfield (instance->ls, -1, "traceback");
+ error = luaL_loadfile (instance->ls, filename);
+ if (error == 0)
+ error = lua_pcall (instance->ls, 0, 0, 0);
+
+ if (error != 0)
+ sch_lua_error (instance->ls, error);
+
+ while (lua_gettop (instance->ls))
+ lua_pop (instance->ls, 1);
+
+ /* Stop processing files if there has been an error */
+ if (error != 0)
+ {
+ syslog (LOG_ERR, "XML: Invalid file \"%s\"\n", filename);
+ g_free (filename);
+ res = false;
+ goto exit;
+ }
+ g_free (filename);
+ }
+ }
+
+ exit:
+ closedir (dir);
+ return res;
+}
+
+
/* Parse all XML files in the search path and merge trees */
static sch_instance *
_sch_load (const char *path, const char *model_list_filename)
@@ -638,6 +895,11 @@ _sch_load (const char *path, const char *model_list_filename)
sch_load_namespace_mappings (instance, filename);
continue;
}
+ if (g_strcmp0 (ext, ".xlat") == 0)
+ {
+ sch_load_model_xlat (instance, filename);
+ continue;
+ }
xmlDoc *doc_new = xmlParseFile (filename);
if (doc_new == NULL)
{
@@ -670,6 +932,8 @@ _sch_load (const char *path, const char *model_list_filename)
/* Store a link back to the instance in the xmlDoc stucture */
instance->doc->_private = (void *) instance;
+ sch_load_script_files (instance, path);
+
return instance;
}
@@ -755,8 +1019,13 @@ sch_free (sch_instance * instance)
g_hash_table_destroy (instance->map_hash_table);
if (instance->model_hash_table)
g_hash_table_destroy (instance->model_hash_table);
+ if (instance->xlat_hash_table)
+ g_hash_table_destroy (instance->xlat_hash_table);
if (instance->regexes)
g_list_free_full (instance->regexes, (GDestroyNotify) free_regex);
+ if (instance->ls)
+ lua_close (instance->ls);
+
g_free (instance);
}
}
@@ -2753,7 +3022,8 @@ _sch_gnode_to_xml (sch_instance * instance, sch_node * schema, xmlNs *ns, xmlNod
}
/* Record any changes to the namespace (including the root node) */
- if (data && schema && (!pschema || ((xmlNode *)pschema)->ns != ((xmlNode *)schema)->ns))
+ if (data && schema && ((xmlNode *)schema)->ns &&
+ (!pschema || ((xmlNode *)pschema)->ns != ((xmlNode *)schema)->ns))
{
/* Dont store a prefix as we set the default xmlns at each node */
xmlNsPtr nns = xmlNewNs (data, ((xmlNode *)schema)->ns->href, NULL);
@@ -2873,12 +3143,41 @@ _operation_ok (_sch_xml_to_gnode_parms *_parms, xmlNode *xml, char *curr_op, cha
static void
_perform_actions (_sch_xml_to_gnode_parms *_parms, int depth, char *curr_op, char *new_op, char *new_xpath)
{
+ void *xlat_data = NULL;
+ char *tmp_xpath;
+
/* Do nothing if not an edit, or operation not changing, unless depth is 0. */
if (!_parms->in_is_edit || (g_strcmp0 (curr_op, new_op) == 0 && depth != 0))
{
return;
}
+ tmp_xpath = NULL;
+ if (_parms->in_instance->xlat_hash_table && g_strcmp0 (new_op, "merge") != 0)
+ {
+ char *name = first_name_in_path (new_xpath);
+ if (g_hash_table_lookup (_parms->in_instance->xlat_hash_table, name))
+ {
+ GNode *orig_query = path_to_tree (new_xpath);
+ int flags = _parms->in_flags;
+
+ xlat_data = NULL;
+ GNode *node = sch_translate_input (_parms->in_instance, orig_query, flags, &xlat_data, NULL);
+ if (node != orig_query)
+ {
+ GNode *last = node;
+ while (last->children)
+ last = last->children;
+ tmp_xpath = apteryx_node_path (last);
+ new_xpath = tmp_xpath;
+ apteryx_free_tree (node);
+ }
+ else
+ apteryx_free_tree (orig_query);
+ }
+ g_free (name);
+ }
+
/* Handle operations. */
if (g_strcmp0 (new_op, "delete") == 0)
{
@@ -2900,6 +3199,9 @@ _perform_actions (_sch_xml_to_gnode_parms *_parms, int depth, char *curr_op, cha
_parms->out_replaces = g_list_append (_parms->out_replaces, g_strdup (new_xpath));
DEBUG (_parms->in_flags, "replace <%s>\n", new_xpath);
}
+
+ if (tmp_xpath)
+ g_free (tmp_xpath);
}
static GNode *
@@ -4065,3 +4367,553 @@ sch_json_to_gnode (sch_instance * instance, sch_node * schema, json_t * json, in
}
return root;
}
+
+static gpointer
+copy_node_data (gconstpointer src, gpointer dummy)
+{
+ char *data = NULL;
+
+ if (src)
+ data = g_strdup (src);
+ return data;
+}
+
+static void
+sch_translate_data (sch_instance * instance, GNode *node, sch_xlat_data *xlat_data, int flags, bool incoming)
+{
+ int ret;
+ int s_0 = lua_gettop (instance->ls);
+
+ lua_getglobal (instance->ls, xlat_data->lua_module);
+ lua_getfield (instance->ls, -1, xlat_data->lua_func);
+ if (!lua_isnil (instance->ls, -1))
+ {
+ lua_pushstring (instance->ls, APTERYX_NAME (node));
+ lua_pushstring (instance->ls, APTERYX_NAME (node->children));
+ lua_pushstring (instance->ls, incoming ? "in" : "out");
+ ret = lua_pcall (instance->ls, 3, 1, 0);
+ if (ret)
+ sch_lua_error (instance->ls, ret);
+ else if (lua_gettop (instance->ls) != (s_0 + 2))
+ {
+ syslog (LOG_ERR, "XML LUA: Stack not zero(%d) after function: %s.%s\n",
+ lua_gettop (instance->ls), xlat_data->lua_module,
+ xlat_data->lua_func);
+ lua_pop (instance->ls, 1);
+ }
+ else
+ {
+ const char *value = lua_tostring (instance->ls, -1);
+ char *old_value = (char *) node->children->data;
+ node->children->data = g_strdup (value);
+ DEBUG (flags, "function: %s old value %s new value %s\n", xlat_data->lua_func, old_value, value);
+ g_free (old_value);
+ }
+ }
+ else
+ {
+ lua_pop(instance->ls, 1);
+ return;
+ }
+}
+
+static bool
+sch_translate_child_exists (GNode *parent, char *name, GNode **ret_node)
+{
+ GNode *child;
+
+ for (child = parent->children; child; child = child->next)
+ {
+ if (g_strcmp0 (APTERYX_NAME (child), name) == 0)
+ {
+ *ret_node = child;
+ return true;
+ }
+ }
+
+ *ret_node = NULL;
+ return false;
+}
+
+static bool
+sch_translate_sibling_exists (GNode *node, char *name, GNode **ret_node)
+{
+ while (node)
+ {
+ if (g_strcmp0 (APTERYX_NAME (node), name) == 0)
+ {
+ *ret_node = node;
+ return true;
+ }
+ node = node->next;
+ }
+
+ *ret_node = NULL;
+ return false;
+}
+
+static bool
+xlat_tree_match (GNode *tree, GNode *pattern, GNode **last_match_node)
+{
+ bool match = false;
+ GNode *node1 = tree;
+ GNode *node2 = pattern;
+ GNode *ret_node;
+ GNode *last_valid_node = NULL;
+
+ while (node1 && node2)
+ {
+ match = true;
+ if (!sch_translate_sibling_exists (node1, APTERYX_NAME (node2), &ret_node) &&
+ g_strcmp0 ((char *) node2->data, "*") != 0)
+ return false;
+ if (ret_node)
+ {
+ last_valid_node = ret_node;
+ node1 = ret_node->children;
+ ret_node = NULL;
+ }
+ else
+ {
+ last_valid_node = node1;
+ node1 = node1->children;
+ }
+ node2 = node2->children;
+ }
+
+ if (node2)
+ match = false;
+ else
+ *last_match_node = last_valid_node;
+
+ return match;
+}
+
+static char *
+sch_translate_get_wildcard_data (GNode *node, GNode *tree, uint32_t pos)
+{
+ GNode *nnode = tree;
+ uint32_t matches = 0;
+
+ while (nnode && node)
+ {
+ if (g_strcmp0 (APTERYX_NAME (nnode), "*") == 0)
+ {
+ matches++;
+ if (matches == pos)
+ return g_strdup (APTERYX_NAME (node));
+ }
+
+ nnode = nnode->children;
+ node = node->children;
+ }
+ return NULL;
+}
+
+static inline gboolean
+_sch_translate_count_nodes (GNode *node, void *data)
+{
+ int *num_nodes = data;
+
+ if (node->data)
+ *num_nodes = *num_nodes + 1;
+
+ return false;
+}
+
+static inline gboolean
+_sch_translate_mark_children (GNode *node, gpointer data)
+{
+ GHashTable *node_table = data;
+ if (!g_hash_table_lookup (node_table, node))
+ g_hash_table_insert (node_table, node, node);
+
+ return false;
+}
+
+static void
+sch_translate_mark_children (GHashTable *node_table, GNode *node)
+{
+ if (node->children)
+ {
+ if (!g_hash_table_lookup (node_table, node))
+ g_node_traverse (node->children, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
+ _sch_translate_mark_children, node_table);
+ }
+}
+
+static void
+sch_translate_mark_node (GHashTable *node_table, GNode *node)
+{
+ GNode *parent;
+
+ if (!g_hash_table_lookup (node_table, node))
+ {
+ g_hash_table_insert (node_table, node, node);
+ parent = node->parent;
+ while (parent)
+ {
+ if (!g_hash_table_lookup (node_table, parent))
+ g_hash_table_insert (node_table, parent, parent);
+
+ parent = parent->parent;
+ }
+ }
+}
+
+static sch_node *
+sch_translate_schema (sch_instance * instance, GNode *node, int flags)
+{
+ sch_node *schema;
+ char *colon;
+ xmlNs *ns = NULL;
+ char *name = APTERYX_NAME (node);
+ if (name[0] == '/')
+ name = name + 1;
+
+ /* Check for a change in namespace */
+ schema = xmlDocGetRootElement (instance->doc);
+ colon = strchr (name, ':');
+ if (colon)
+ {
+ char *namespace = g_strndup (name, colon - name);
+ xmlNs *nns = sch_lookup_ns (instance, schema, namespace, flags, false);
+ free (namespace);
+ if (nns)
+ {
+ /* We found a namespace. Skip the prefix */
+ name = colon + 1;
+ ns = nns;
+ }
+ }
+
+ /* Find schema node */
+ schema = _sch_node_child (ns, schema, name);
+ if (schema == NULL)
+ {
+ ERROR (flags, SCH_E_NOSCHEMANODE, "No schema match for node %s\n", name);
+ return NULL;
+ }
+ return schema;
+}
+
+static bool
+sch_translate_input_matches (sch_instance * instance, GHashTable *node_table, GNode *start_node,
+ int flags, bool exact, GNode **node, sch_xlat_data *x_data,
+ sch_node **rschema)
+{
+ GNode *nnode;
+ GNode *int_node;
+ GNode *ret_node;
+ GNode *remaining = NULL;
+ GNode *last_match_node = NULL;
+ bool match = false;
+
+ if (xlat_tree_match (start_node, x_data->ext_tree, &last_match_node))
+ {
+ if (g_hash_table_lookup (node_table, last_match_node))
+ return match;
+
+ sch_translate_mark_node (node_table, last_match_node);
+ if (last_match_node && last_match_node->children)
+ {
+ remaining = g_node_copy_deep (last_match_node->children, copy_node_data, NULL);
+ sch_translate_mark_children (node_table, last_match_node);
+ }
+
+ int_node = x_data->int_tree;
+ if (rschema && !*rschema)
+ {
+ *rschema = sch_translate_schema (instance, int_node, flags);
+ }
+
+ if (!*node)
+ *node = g_node_new (g_strdup (APTERYX_NAME (int_node)));
+ nnode = *node;
+ int_node = int_node->children;
+ while (int_node)
+ {
+ if (g_strcmp0 (APTERYX_NAME (int_node), "*") == 0)
+ {
+ char *str = sch_translate_get_wildcard_data (start_node, x_data->ext_tree, 1);
+ if (sch_translate_child_exists (nnode, str, &ret_node))
+ {
+ g_free (str);
+ nnode = ret_node;
+ }
+ else
+ nnode = g_node_append_data (nnode, str);
+ }
+ else
+ {
+ if (sch_translate_child_exists (nnode, APTERYX_NAME (int_node), &ret_node))
+ nnode = ret_node;
+ else
+ nnode = g_node_append_data (nnode, g_strdup (APTERYX_NAME (int_node)));
+ }
+
+ int_node = int_node->children;
+ }
+ if (remaining)
+ {
+ if (sch_translate_child_exists (nnode, APTERYX_NAME (remaining), &ret_node))
+ {
+ nnode = g_node_append (ret_node, remaining);
+ }
+ else
+ nnode = g_node_append (nnode, remaining);
+ }
+
+ if (x_data->lua_module && nnode)
+ sch_translate_data (instance, nnode->parent, x_data, flags, true);
+
+ match = true;
+ }
+
+ return match;
+}
+
+GNode *
+sch_translate_input (sch_instance * instance, GNode *node, int flags, void **xlat_data, sch_node **rschema)
+{
+ GNode *ret_node = NULL;
+ char *name;
+ sch_xlat_root *root;
+ GHashTable *node_table = NULL;
+ GList *list;
+ sch_xlat_data *x_data;
+ bool match;
+ int num_nodes = 0;
+ int query_depth = 0;
+
+ *xlat_data = NULL;
+ if (!instance->xlat_hash_table || !node)
+ return node;
+
+ name = APTERYX_NAME (node);
+ root = g_hash_table_lookup (instance->xlat_hash_table, name);
+
+ if (!root)
+ return node;
+
+ if (rschema)
+ {
+ *rschema = NULL;
+ query_depth = g_node_max_height (node);
+ }
+
+ node_table = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
+ g_node_traverse (node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, _sch_translate_count_nodes, &num_nodes);
+
+ for (list = g_list_first (root->matchs); list; list = g_list_next (list))
+ {
+ x_data = list->data;
+ match = sch_translate_input_matches (instance, node_table, node, flags, true,
+ &ret_node, x_data, rschema);
+ if (match)
+ {
+ if (!*xlat_data)
+ *xlat_data = x_data;
+
+ if (num_nodes <= g_hash_table_size (node_table))
+ break;
+ }
+ }
+ g_hash_table_destroy (node_table);
+
+ if (ret_node)
+ {
+ /* Restconf delete requires the translated schema node */
+ if (rschema && *rschema)
+ {
+ sch_node *schema = *rschema;
+ GNode *node1 = ret_node;
+ for (int depth = 1; depth < query_depth && node1; depth++)
+ {
+ node1 = node1->children;
+ if (node1)
+ {
+ schema = _sch_node_child (NULL, schema, APTERYX_NAME (node1));
+ }
+ }
+ if (schema)
+ *rschema = schema;
+ }
+ apteryx_free_tree (node);
+
+ return ret_node;
+ }
+
+ return node;
+}
+
+static char *
+sch_translate_get_wildcard_data_from_bottom (GNode *node, GNode *tree)
+{
+ GNode *nnode = tree;
+
+ /* Move to the bottom of the tree */
+ while (nnode && nnode->children)
+ nnode = nnode->children;
+
+ while (nnode && node)
+ {
+ if (g_strcmp0 (APTERYX_NAME (nnode), "*") == 0)
+ return g_strdup (APTERYX_NAME (node));
+
+ nnode = nnode->parent;
+ node = node->parent;
+ }
+ return NULL;
+}
+
+static void
+sch_translate_merge_output (GNode **ret_node, GNode *node, GHashTable *node_table,
+ sch_xlat_data *xlat_data)
+{
+ GNode *nnode;
+ GNode *node1;
+ GNode *copy;
+
+ /* Write/move down the output tree until we hit a wildcard or end of path */
+ // nnode = ret_node;
+ node1 = xlat_data->ext_tree;
+ if (!*ret_node)
+ *ret_node = g_node_new (g_strdup (APTERYX_NAME (node1)));
+ nnode = *ret_node;
+ while (node1)
+ {
+ if (g_strcmp0 (APTERYX_NAME (node1), "*") == 0)
+ break;
+
+ if (nnode && g_strcmp0 (APTERYX_NAME (nnode), APTERYX_NAME (node1)) == 0)
+ {
+ if (nnode->children && node1->children && g_strcmp0 (APTERYX_NAME (node1->children), "*") != 0)
+ nnode = nnode->children;
+ }
+ else
+ nnode = g_node_append_data (nnode, g_strdup (APTERYX_NAME (node1)));
+
+ node1 = node1->children;
+ }
+
+ /* Move down the input tree until we hit a wildcard or end of path */
+ if (node1)
+ {
+ char *str = sch_translate_get_wildcard_data_from_bottom (node, xlat_data->int_tree);
+ if (str)
+ {
+ GNode *new_nnode;
+ if (sch_translate_child_exists (nnode, str, &new_nnode))
+ {
+ nnode = new_nnode;
+ g_free (str);
+ }
+ else
+ nnode = g_node_append_data (nnode, str);
+ }
+
+ node1 = node1->children;
+ }
+
+ /* We should now be synchronized at a wildcard in both int and external trees,
+ add in fields after the wild card */
+ while (node1 && node1->children)
+ {
+ GNode *new_nnode;
+
+ if (nnode && sch_translate_child_exists (nnode, APTERYX_NAME (node1), &new_nnode))
+ nnode = new_nnode;
+ else
+ nnode = g_node_append_data (nnode, g_strdup (APTERYX_NAME (node1)));
+
+ node1 = node1->children;
+ }
+
+ /* Now copy the exact match node to the output tree */
+ copy = g_node_copy_deep (node, copy_node_data, NULL);
+ sch_translate_mark_node (node_table, node);
+ sch_translate_mark_children (node_table, node);
+
+ /* Check if a field name change is required */
+ if (node1)
+ {
+ if (g_strcmp0 (APTERYX_NAME (copy), APTERYX_NAME (node1)))
+ {
+ g_free (copy->data);
+ copy->data = g_strdup (APTERYX_NAME (node1));
+ }
+ }
+
+ nnode = g_node_append (nnode, copy);
+}
+
+static void
+sch_translate_output_matches (sch_instance * instance, GNode **ret_node, GNode *node,
+ GNode *int_node, GHashTable *node_table, int flags,
+ sch_xlat_data *xlat_data)
+{
+ GNode *child;
+ GNode *next_child;
+
+ if (g_strcmp0 ((char *) node->data, (char *) int_node->data) &&
+ g_strcmp0 ((char *) int_node->data, "*"))
+ return;
+
+ if (!int_node->children && g_hash_table_lookup (node_table, node))
+ return;
+
+ int_node = int_node->children;
+ if (!int_node)
+ {
+ if (xlat_data->lua_module && node->children)
+ sch_translate_data (instance, node, xlat_data, flags, false);
+
+ /* We have an exact match, translate the path into the output tree */
+ sch_translate_merge_output (ret_node, node, node_table, xlat_data);
+
+ return;
+ }
+
+ for (child = node->children; child; child = next_child)
+ {
+ next_child = child->next;
+ sch_translate_output_matches (instance, ret_node, child, int_node, node_table,
+ flags, xlat_data);
+ }
+}
+
+GNode *
+sch_translate_output (sch_instance * instance, GNode *node, int flags, void *xlat_data)
+{
+ sch_xlat_data *x_data = (sch_xlat_data *) xlat_data;
+ sch_xlat_root *root;
+ GList *list;
+ GHashTable *node_table = NULL;
+ int num_nodes;
+ GNode *ret_node = NULL;
+
+ if (!instance->xlat_hash_table || !node)
+ return node;
+
+ if (!x_data)
+ return node;
+
+ root = x_data->root;
+ node_table = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
+
+ num_nodes = g_node_n_nodes (node, G_TRAVERSE_ALL);
+ for (list = g_list_first (root->matchs); list; list = g_list_next (list))
+ {
+ x_data = list->data;
+ sch_translate_output_matches (instance, &ret_node, node, x_data->int_tree, node_table,
+ flags, x_data);
+ if (num_nodes <= g_hash_table_size (node_table))
+ break;
+ }
+
+ g_hash_table_destroy (node_table);
+ apteryx_free_tree (node);
+
+ return ret_node;
+}