Add an example demonstrating use of sequencer API.
[calfbox.git] / dom.c
blob54214c9716c25dc8c4148df885c2f9d1b6106c27
1 /*
2 Calf Box, an open source musical instrument.
3 Copyright (C) 2012 Krzysztof Foltman
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "cmd.h"
20 #include "errors.h"
21 #include "dom.h"
23 #include <assert.h>
24 #include <glib.h>
25 #include <malloc.h>
26 #include <string.h>
28 static guint cbox_uuid_hash(gconstpointer v);
29 static gboolean cbox_uuid_equal(gconstpointer v1, gconstpointer v2);
31 static GHashTable *class_name_hash = NULL;
33 struct cbox_class_per_document
35 GList *instances;
38 struct cbox_document
40 GHashTable *classes_per_document;
41 GHashTable *services_per_document;
42 GHashTable *uuids_per_document;
43 struct cbox_command_target cmd_target;
44 int item_ctr;
47 ////////////////////////////////////////////////////////////////////////////////////////
49 void cbox_dom_init()
51 class_name_hash = g_hash_table_new(g_str_hash, g_str_equal);
54 void cbox_dom_close()
56 g_hash_table_destroy(class_name_hash);
59 ////////////////////////////////////////////////////////////////////////////////////////
61 struct cbox_class *cbox_class_find_by_name(const char *name)
63 assert(class_name_hash != NULL);
64 return g_hash_table_lookup(class_name_hash, name);
67 void cbox_class_register(struct cbox_class *class_ptr)
69 assert(class_name_hash != NULL);
70 g_hash_table_insert(class_name_hash, (gpointer)class_ptr->name, class_ptr);
73 static struct cbox_class_per_document *get_cpd_for_class(struct cbox_document *doc, struct cbox_class *class_ptr)
75 struct cbox_class_per_document *p = g_hash_table_lookup(doc->classes_per_document, class_ptr);
76 if (p != NULL)
77 return p;
78 p = malloc(sizeof(struct cbox_class_per_document));
79 p->instances = NULL;
80 g_hash_table_insert(doc->classes_per_document, class_ptr, p);
81 return p;
84 ////////////////////////////////////////////////////////////////////////////////////////
86 guint cbox_uuid_hash(gconstpointer v)
88 char buf[40];
89 uuid_unparse_lower(((struct cbox_uuid *)v)->uuid, buf);
90 return g_str_hash(buf);
93 gboolean cbox_uuid_equal(gconstpointer v1, gconstpointer v2)
95 const struct cbox_uuid *p1 = v1;
96 const struct cbox_uuid *p2 = v2;
98 return !uuid_compare(p1->uuid, p2->uuid);
101 ////////////////////////////////////////////////////////////////////////////////////////
103 void cbox_object_register_instance(struct cbox_document *doc, struct cbox_objhdr *obj)
105 assert(obj != NULL);
107 struct cbox_class_per_document *cpd = get_cpd_for_class(doc, obj->class_ptr);
108 cpd->instances = g_list_prepend(cpd->instances, obj);
109 obj->owner = doc;
110 obj->link_in_document = cpd->instances;
111 g_hash_table_insert(obj->owner->uuids_per_document, &obj->instance_uuid, obj);
114 struct cbox_command_target *cbox_object_get_cmd_target(struct cbox_objhdr *hdr_ptr)
116 if (!hdr_ptr->class_ptr->getcmdtargetfunc)
117 return NULL;
118 return hdr_ptr->class_ptr->getcmdtargetfunc(hdr_ptr);
121 gboolean cbox_object_try_default_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, const char *subcmd, gboolean *result, GError **error)
123 // XXXKF this assumes objhdr ptr == object ptr - needs to add the header offset in cmd target?
124 struct cbox_objhdr *obj = ct->user_data;
125 if (!strcmp(subcmd, "/status") && !strcmp(cmd->arg_types, ""))
127 if (!cbox_object_default_status(obj, fb, error))
129 *result = FALSE;
130 return TRUE;
132 return FALSE;
134 if (!strcmp(subcmd, "/delete") && !strcmp(cmd->arg_types, ""))
136 cbox_object_destroy(obj);
137 *result = TRUE;
138 return TRUE;
140 if (!strcmp(subcmd, "/get_uuid") && !strcmp(cmd->arg_types, ""))
142 if (!cbox_check_fb_channel(fb, cmd->command, error))
144 *result = FALSE;
145 return TRUE;
148 char buf[40];
149 uuid_unparse(obj->instance_uuid.uuid, buf);
150 *result = cbox_execute_on(fb, NULL, "/uuid", "s", error, buf);
151 return TRUE;
153 if (!strcmp(subcmd, "/get_class_name") && !strcmp(cmd->arg_types, ""))
155 if (!cbox_check_fb_channel(fb, cmd->command, error))
157 *result = FALSE;
158 return TRUE;
160 *result = cbox_execute_on(fb, NULL, "/class_name", "s", error, obj->class_ptr->name);
161 return TRUE;
163 return FALSE;
166 gboolean cbox_object_default_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
168 gboolean result = FALSE;
169 if (cbox_object_try_default_process_cmd(ct, fb, cmd, cmd->command, &result, error))
170 return result;
171 struct cbox_objhdr *obj = ct->user_data;
172 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown combination of target path and argument: '%s', '%s' for object class '%s'", cmd->command, cmd->arg_types, obj->class_ptr->name);
173 return FALSE;
176 gboolean cbox_object_default_status(struct cbox_objhdr *objhdr, struct cbox_command_target *fb, GError **error)
178 char buf[40];
179 uuid_unparse(objhdr->instance_uuid.uuid, buf);
180 return cbox_execute_on(fb, NULL, "/uuid", "s", error, buf);
183 void cbox_object_destroy(struct cbox_objhdr *hdr_ptr)
185 struct cbox_class_per_document *cpd = get_cpd_for_class(hdr_ptr->owner, hdr_ptr->class_ptr);
186 cpd->instances = g_list_remove_link(cpd->instances, hdr_ptr->link_in_document);
187 hdr_ptr->link_in_document = NULL;
188 g_hash_table_remove(hdr_ptr->owner->uuids_per_document, &hdr_ptr->instance_uuid);
190 hdr_ptr->class_ptr->destroyfunc(hdr_ptr);
193 ////////////////////////////////////////////////////////////////////////////////////////
195 static gboolean document_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
197 char *uuid;
198 const char *subcommand;
199 if (!strcmp(cmd->command, "/dump") && !strcmp(cmd->arg_types, ""))
201 struct cbox_document *doc = ct->user_data;
202 cbox_document_dump(doc);
203 return TRUE;
205 if (cbox_parse_path_part_str(cmd, "/uuid/", &subcommand, &uuid, error))
207 struct cbox_document *doc = ct->user_data;
208 if (!subcommand)
209 return FALSE;
210 struct cbox_objhdr *obj = cbox_document_get_object_by_text_uuid(doc, uuid, NULL, error);
211 g_free(uuid);
212 if (!obj)
213 return FALSE;
214 struct cbox_command_target *ct2 = cbox_object_get_cmd_target(obj);
215 return cbox_execute_sub(ct2, fb, cmd, subcommand, error);
217 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown combination of target path and argument: '%s', '%s'", cmd->command, cmd->arg_types);
218 return FALSE;
221 struct cbox_document *cbox_document_new()
223 struct cbox_document *res = malloc(sizeof(struct cbox_document));
224 res->classes_per_document = g_hash_table_new(NULL, NULL);
225 res->services_per_document = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
226 res->uuids_per_document = g_hash_table_new(cbox_uuid_hash, cbox_uuid_equal);
227 res->cmd_target.process_cmd = document_process_cmd;
228 res->cmd_target.user_data = res;
229 res->item_ctr = 0;
231 return res;
234 struct cbox_command_target *cbox_document_get_cmd_target(struct cbox_document *doc)
236 return &doc->cmd_target;
239 struct cbox_objhdr *cbox_document_get_service(struct cbox_document *document, const char *name)
241 return g_hash_table_lookup(document->services_per_document, name);
244 void cbox_document_set_service(struct cbox_document *document, const char *name, struct cbox_objhdr *obj)
246 g_hash_table_insert(document->services_per_document, g_strdup(name), obj);
249 struct cbox_objhdr *cbox_document_get_object_by_uuid(struct cbox_document *doc, const struct cbox_uuid *uuid)
251 return g_hash_table_lookup(doc->uuids_per_document, uuid);
254 struct cbox_objhdr *cbox_document_get_object_by_text_uuid(struct cbox_document *doc, const char *uuid, const struct cbox_class *class_ptr, GError **error)
256 struct cbox_uuid uuidv;
257 if (uuid_parse(uuid, uuidv.uuid))
259 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Malformed UUID: '%s'", uuid);
260 return NULL;
262 struct cbox_objhdr *obj = cbox_document_get_object_by_uuid(doc, &uuidv);
263 if (!obj)
265 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "UUID not found: '%s'", uuid);
266 return NULL;
268 if (class_ptr && !cbox_class_is_a(obj->class_ptr, class_ptr))
270 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unexpected object type '%s' for UUID '%s' (expected '%s')", obj->class_ptr->name, uuid, class_ptr->name);
271 return NULL;
273 return obj;
276 static void iter_func(gpointer key, gpointer value, gpointer doc_)
278 struct cbox_document *doc = (struct cbox_document *)doc_;
279 struct cbox_class *class_ptr = key;
280 struct cbox_class_per_document *cpd = value;
281 int first = 1;
282 printf("Class %s: ", class_ptr->name);
283 GList *l = cpd->instances;
284 while(l) {
285 if (!first)
286 printf(", ");
287 printf("%p", l->data);
288 fflush(stdout);
289 struct cbox_objhdr *hdr = (struct cbox_objhdr *)l->data;
290 char buf[40];
291 uuid_unparse(hdr->instance_uuid.uuid, buf);
292 printf("[%s]", buf);
293 fflush(stdout);
294 assert(cbox_document_get_object_by_uuid(doc, &hdr->instance_uuid));
295 l = l->next;
296 first = 0;
298 if (first)
299 printf("<no instances>");
300 printf("\n");
303 static void iter_func2(gpointer key, gpointer value, gpointer document)
305 struct cbox_objhdr *oh = value;
306 char buf[40];
307 uuid_unparse(oh->instance_uuid.uuid, buf);
308 int first = 1;
309 printf("Service %s: %p", (const char *)key, value);
310 fflush(stdout);
311 printf("[%s]", buf);
312 fflush(stdout);
313 printf(" (%s)\n", oh->class_ptr->name);
316 void cbox_document_dump(struct cbox_document *document)
318 g_hash_table_foreach(document->classes_per_document, iter_func, document);
319 g_hash_table_foreach(document->services_per_document, iter_func2, document);
322 void cbox_document_destroy(struct cbox_document *document)
324 g_hash_table_destroy(document->classes_per_document);
325 g_hash_table_destroy(document->services_per_document);
326 g_hash_table_destroy(document->uuids_per_document);
327 free(document);