[asterisk-commits] file: branch file/sorcery r378071 - in /team/file/sorcery: include/asterisk/ ...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Mon Dec 17 10:03:51 CST 2012
Author: file
Date: Mon Dec 17 10:03:45 2012
New Revision: 378071
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=378071
Log:
I went crazy this weekend. This implements most stuff, except retrieval, and adds tests. This is still very much in flux!
Added:
team/file/sorcery/res/res_sorcery_memory.c (with props)
Modified:
team/file/sorcery/include/asterisk/sorcery.h
team/file/sorcery/main/sorcery.c
team/file/sorcery/tests/test_sorcery.c
Modified: team/file/sorcery/include/asterisk/sorcery.h
URL: http://svnview.digium.com/svn/asterisk/team/file/sorcery/include/asterisk/sorcery.h?view=diff&rev=378071&r1=378070&r2=378071
==============================================================================
--- team/file/sorcery/include/asterisk/sorcery.h (original)
+++ team/file/sorcery/include/asterisk/sorcery.h Mon Dec 17 10:03:45 2012
@@ -36,11 +36,29 @@
extern "C" {
#endif
+#include "asterisk/config_options.h"
+
/*! \brief Maximum size of an object unique identifier */
#define MAX_OBJECT_ID 64
/*! \brief Maximum size of an object type */
#define MAX_OBJECT_TYPE 64
+
+/*! \brief Forward declaration for an object wizard instance */
+struct ast_sorcery_object_wizard;
+
+/*!
+ * \brief A callback function for translating a value into a string
+ *
+ * \param obj Object to get value from
+ * \param args Where the field is
+ * \param buf Buffer to place string into
+ * \param len Size of the buffer
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+typedef int (*sorcery_field_handler)(const void *obj, const intptr_t *args, char *buf, size_t len);
/*! \brief Interface for a sorcery wizard */
struct ast_sorcery_wizard {
@@ -49,6 +67,21 @@
/*! \brief Pointer to the Asterisk module this wizard is implemented by */
struct ast_module *module;
+
+ /*! \brief Callback for opening a wizard */
+ void *(*open)(const char *data);
+
+ /*! \brief Callback for creating an object */
+ int (*create)(void *data, void *object);
+
+ /*! \brief Callback for updating an object */
+ int (*update)(void *data, void *object);
+
+ /*! \brief Callback for deleting an object */
+ int (*delete)(void *data, void *object);
+
+ /*! \brief Callback for closing a wizard */
+ void (*close)(void *data);
};
/*! \brief Forward declaration for the sorcery main structure */
@@ -62,8 +95,8 @@
/*! \brief Type of object */
char type[MAX_OBJECT_TYPE];
- /*! \brief Wizard which has conjured this object */
- struct ast_sorcery_wizard *wizard;
+ /*! \brief Object wizard which has conjured this object */
+ struct ast_sorcery_object_wizard *wizard;
};
/*! \brief Macro which must be used at the beginning of each sorcery capable object */
@@ -104,12 +137,21 @@
/*!
* \brief Open a new sorcery structure
*
- * \param name Optional name of the module using the sorcery API, used for explicit object->wizard mappings
- *
* \retval non-NULL success
* \retval NULL if allocation failed
*/
-struct ast_sorcery *ast_sorcery_open(const char *name);
+struct ast_sorcery *ast_sorcery_open(void);
+
+/*!
+ * \brief Apply configured wizard mappings
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param name Name of the category to use within the configuration file, normally the module name
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_sorcery_apply_config(struct ast_sorcery *sorcery, const char *name);
/*!
* \brief Apply default object wizard mappings
@@ -129,11 +171,110 @@
int ast_sorcery_apply_default(struct ast_sorcery *sorcery, const char *type, const char *name, const char *data);
/*!
+ * \brief Register an object type
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object
+ * \param aco_type Configuration framework details for type
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_sorcery_object_register(struct ast_sorcery *sorcery, const char *type, struct aco_type *aco_type);
+
+/*!
+ * \brief Register a field within an object
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object
+ * \param name Name of the field
+ * \param default_val Default value of the field
+ * \param opt_type Option type
+ * \param flags Option type specific flags
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int __ast_sorcery_object_field_register(struct ast_sorcery *sorcery, const char *type, const char *name, const char *default_val, enum aco_option_type opt_type,
+ aco_option_handler config_handler, sorcery_field_handler sorcery_handler, unsigned int flags, size_t argc, ...);
+
+/*!
+ * \brief Register a field within an object
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object
+ * \param name Name of the field
+ * \param default_val Default value of the field
+ * \param opt_type Option type
+ * \param flags Option type specific flags
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+#define ast_sorcery_object_field_register(sorcery, type, name, default_val, opt_type, flags, ...) \
+ __ast_sorcery_object_field_register(sorcery, type, name, default_val, opt_type, NULL, NULL, flags, VA_NARGS(__VA_ARGS__), __VA_ARGS__);
+
+/*!
+ * \brief Register a field within an object with custom handlers
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object
+ * \param name Name of the field
+ * \param default_val Default value of the field
+ * \param config_handler Custom configuration handler
+ * \param sorcery_handler Custom sorcery handler
+ * \param flags Option type specific flags
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+#define ast_sorcery_object_field_register_custom(sorcery, type, name, default_val, config_handler, sorcery_handler, flags, ...) \
+ __ast_sorcery_object_field_register(sorcery, type, name, default_val, OPT_CUSTOM_T, config_handler, sorcery_handler, flags, VA_NARGS(__VA_ARGS__), __VA_ARGS__);
+
+/*!
* \brief Increase the reference count of a sorcery structure
*
* \param sorcery Pointer to a sorcery structure
*/
void ast_sorcery_ref(struct ast_sorcery *sorcery);
+
+/*!
+ * \brief Create an object set (KVP list) for an object
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param object Pointer to a sorcery object
+ *
+ * \retval non-NULL success
+ * \retval NULL if error occurred
+ *
+ * \note The reference to the returned container must be released using ao2_ref or ao2_cleanup
+ */
+struct ast_variable *ast_sorcery_objectset_create(const struct ast_sorcery *sorcery, const void *object);
+
+/*!
+ * \brief Apply an object set (KVP list) to an object
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param object Pointer to a sorcery object
+ * \param objectset Object set itself
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_sorcery_objectset_apply(const struct ast_sorcery *sorcery, void *object, struct ast_variable *objectset);
+
+/*!
+ * \brief Create a changeset given two object sets
+ *
+ * \param original Original object set
+ * \param modified Modified object set
+ *
+ * \retval non-NULL if any changes are present
+ * \retval NULL if no changes exist
+ *
+ * \note The returned ast_variable list must be destroyed using ast_variables_destroy
+ */
+struct ast_variable *ast_sorcery_changeset_create(const struct ast_variable *original, const struct ast_variable *modified);
/*!
* \brief Allocate an object
@@ -143,11 +284,38 @@
* \param id Optional unique identifier, if none is provided one will be generated
*
* \retval non-NULL success
- * \retval NULL if allocation failed
+ * \retval NULL failure
*/
void *ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, const char *id);
/*!
+ * \brief Create a copy of an object
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param object Existing object
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ */
+void *ast_sorcery_copy(const struct ast_sorcery *sorcery, const void *object);
+
+/*!
+ * \brief Create a changeset of two objects
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param original Original object
+ * \param modified Modified object
+ *
+ * \retval non-NULL differences exist
+ * \retval NULL no differences exist
+ *
+ * \note The returned ast_variable list must be destroyed using ast_variables_destroy
+ *
+ * \note While the objects must be of the same type they do not have to be the same object
+ */
+ struct ast_variable *ast_sorcery_diff(const struct ast_sorcery *sorcery, const void *original, const void *modified);
+
+/*!
* \brief Persist an object
*
* \param sorcery Pointer to a sorcery structure
@@ -194,7 +362,7 @@
*
* \retval unique identifier
*/
-const char *ast_sorcery_object_get_id(void *object);
+const char *ast_sorcery_object_get_id(const void *object);
/*!
* \brief Get the type of a sorcery object
@@ -203,7 +371,7 @@
*
* \retval type of object
*/
-const char *ast_sorcery_object_get_type(void *object);
+const char *ast_sorcery_object_get_type(const void *object);
#if defined(__cplusplus) || defined(c_plusplus)
}
Modified: team/file/sorcery/main/sorcery.c
URL: http://svnview.digium.com/svn/asterisk/team/file/sorcery/main/sorcery.c?view=diff&rev=378071&r1=378070&r2=378071
==============================================================================
--- team/file/sorcery/main/sorcery.c (original)
+++ team/file/sorcery/main/sorcery.c Mon Dec 17 10:03:45 2012
@@ -36,6 +36,8 @@
#include "asterisk/astobj2.h"
#include "asterisk/strings.h"
#include "asterisk/config_options.h"
+#include "asterisk/netsock2.h"
+#include "asterisk/module.h"
/*! \brief Number of buckets for wizards */
#define WIZARD_BUCKETS 7
@@ -43,16 +45,40 @@
/*! \brief Number of buckets for types */
#define TYPE_BUCKETS 53
+/*! \brief Maximum length of an object field name */
+#define MAX_OBJECT_FIELD 128
+
/*! \brief Structure for registered object type */
struct ast_sorcery_object_type {
- /*! \brief Unqiue name of the object type */
+ /*! \brief Unique name of the object type */
char name[MAX_OBJECT_TYPE];
/*! \brief Wizard instances */
struct ao2_container *wizards;
+ /*! \brief Object fields */
+ struct ao2_container *fields;
+
+ /*! \brief Configuration framework general information */
+ struct aco_info *info;
+
+ /*! \brief Configuration framework file information */
+ struct aco_file *file;
+
/*! \brief Pointer to the configuration framework details for the object */
struct aco_type *type;
+};
+
+/*! \brief Structure for registered object field */
+struct ast_sorcery_object_field {
+ /*! \brief Name of the field */
+ char name[MAX_OBJECT_FIELD];
+
+ /*! \brief Callback function for translation */
+ sorcery_field_handler handler;
+
+ /*! \brief Position of the field */
+ intptr_t args[0];
};
/*! \brief Structure for a wizard instance which operates on objects */
@@ -73,6 +99,79 @@
/*! \brief Registered sorcery wizards */
struct ao2_container *wizards;
+static int int_handler_fn(const void *obj, const intptr_t *args, char *buf, size_t len)
+{
+ int *field = (int *)(obj + args[0]);
+ snprintf(buf, len, "%d", *field);
+ return 0;
+}
+
+static int uint_handler_fn(const void *obj, const intptr_t *args, char *buf, size_t len)
+{
+ unsigned int *field = (unsigned int *)(obj + args[0]);
+ snprintf(buf, len, "%u", *field);
+ return 0;
+}
+
+static int double_handler_fn(const void *obj, const intptr_t *args, char *buf, size_t len)
+{
+ double *field = (double *)(obj + args[0]);
+ snprintf(buf, len, "%f", *field);
+ return 0;
+}
+
+static int stringfield_handler_fn(const void *obj, const intptr_t *args, char *buf, size_t len)
+{
+ ast_string_field *field = (const char **)(obj + args[0]);
+ ast_copy_string(buf, *field, len);
+ return 0;
+}
+
+static int bool_handler_fn(const void *obj, const intptr_t *args, char *buf, size_t len)
+{
+ unsigned int *field = (unsigned int *)(obj + args[0]);
+ snprintf(buf, len, *field ? "true" : "false");
+ return 0;
+}
+
+static int sockaddr_handler_fn(const void *obj, const intptr_t *args, char *buf, size_t len)
+{
+ struct ast_sockaddr *field = (struct ast_sockaddr *)(obj + args[0]);
+ snprintf(buf, len, "%s", ast_sockaddr_stringify(field));
+ return 0;
+}
+
+static int noop_handler_fn(const void *obj, const intptr_t *args, char *buf, size_t len)
+{
+ return 0;
+}
+
+static int chararray_handler_fn(const void *obj, const intptr_t *args, char *buf, size_t len)
+{
+ char *field = (char *)(obj + args[0]);
+ ast_copy_string(buf, field, len);
+ return 0;
+}
+
+static sorcery_field_handler sorcery_field_default_handler(enum aco_option_type type)
+{
+ switch(type) {
+ case OPT_BOOL_T: return bool_handler_fn;
+ case OPT_CHAR_ARRAY_T: return chararray_handler_fn;
+ case OPT_DOUBLE_T: return double_handler_fn;
+ case OPT_INT_T: return int_handler_fn;
+ case OPT_NOOP_T: return noop_handler_fn;
+ case OPT_SOCKADDR_T: return sockaddr_handler_fn;
+ case OPT_STRINGFIELD_T: return stringfield_handler_fn;
+ case OPT_UINT_T: return uint_handler_fn;
+
+ default:
+ case OPT_CUSTOM_T: return NULL;
+ }
+
+ return NULL;
+}
+
/*! \brief Hashing function for sorcery wizards */
static int sorcery_wizard_hash(const void *obj, const int flags)
{
@@ -174,7 +273,7 @@
return !strcmp(type1->name, flags & OBJ_KEY ? name : type2->name) ? CMP_MATCH | CMP_STOP : 0;
}
-struct ast_sorcery *ast_sorcery_open(const char *name)
+struct ast_sorcery *ast_sorcery_open(void)
{
struct ast_sorcery *sorcery;
@@ -182,7 +281,7 @@
return NULL;
}
- if (!(sorcery->types = ao2_container_alloc(TYPE_BUCKETS, sorcery_type_hash, sorcery_type_cmp))) {
+ if (!(sorcery->types = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, TYPE_BUCKETS, sorcery_type_hash, sorcery_type_cmp))) {
ao2_ref(sorcery, -1);
sorcery = NULL;
}
@@ -190,17 +289,229 @@
return sorcery;
}
+/*! \brief Destructor function for object types */
+static void sorcery_object_type_destructor(void *obj)
+{
+ struct ast_sorcery_object_type *object_type = obj;
+
+ ao2_cleanup(object_type->wizards);
+ ao2_cleanup(object_type->fields);
+
+ if (object_type->info) {
+ aco_info_destroy(object_type->info);
+ ast_free(object_type->info);
+ }
+
+ if (object_type->file) {
+ ast_free(object_type->file);
+ }
+}
+
/*! \brief Internal function which allocates an object type structure */
+static struct ast_sorcery_object_type *sorcery_object_type_alloc(const char *type)
+{
+ struct ast_sorcery_object_type *object_type;
+
+ if (!(object_type = ao2_alloc(sizeof(*object_type), sorcery_object_type_destructor))) {
+ return NULL;
+ }
+
+ /* Order matters for object wizards */
+ if (!(object_type->wizards = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) {
+ ao2_ref(object_type, -1);
+ return NULL;
+ }
+
+ if (!(object_type->fields = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) {
+ ao2_ref(object_type, -1);
+ return NULL;
+ }
+
+ if (!(object_type->info = ast_calloc(1, sizeof(*object_type->info) + 2 * sizeof(object_type->info->files[0])))) {
+ ao2_ref(object_type, -1);
+ return NULL;
+ }
+
+ if (!(object_type->file = ast_calloc(1, sizeof(*object_type->file) + 2 * sizeof(object_type->file->types[0])))) {
+ ao2_ref(object_type, -1);
+ return NULL;
+ }
+
+ object_type->info->files[0] = object_type->file;
+ object_type->info->files[1] = NULL;
+
+ ast_copy_string(object_type->name, type, sizeof(object_type->name));
+
+ return object_type;
+}
+
+/*! \brief Object wizard destructor */
+static void sorcery_object_wizard_destructor(void *obj)
+{
+ struct ast_sorcery_object_wizard *object_wizard = obj;
+
+ if (object_wizard->data) {
+ object_wizard->wizard->close(object_wizard->data);
+ }
+
+ if (object_wizard->wizard->module) {
+ ast_module_unref(object_wizard->wizard->module);
+ }
+}
+
+/*! \brief Internal function which creates an object type and adds a wizard mapping */
+static int sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery, const char *type, const char *name, const char *data)
+{
+ RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+ RAII_VAR(struct ast_sorcery_wizard *, wizard, ao2_find(wizards, name, OBJ_KEY), ao2_cleanup);
+ RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, NULL, ao2_cleanup);
+ int created = 0;
+
+ if (!wizard) {
+ return -1;
+ }
+
+ if (!object_type) {
+ if (!(object_type = sorcery_object_type_alloc(type))) {
+ return -1;
+ }
+ created = 1;
+ }
+
+ if (!(object_wizard = ao2_alloc(sizeof(*object_wizard), sorcery_object_wizard_destructor))) {
+ return -1;
+ }
+
+ if (!(object_wizard->data = wizard->open(data))) {
+ return -1;
+ }
+
+ if (wizard->module) {
+ ast_module_ref(wizard->module);
+ }
+
+ object_wizard->wizard = wizard;
+
+ ao2_link(object_type->wizards, object_wizard);
+
+ if (created) {
+ ao2_link(sorcery->types, object_type);
+ }
+
+ return 0;
+}
+
+int ast_sorcery_apply_config(struct ast_sorcery *sorcery, const char *name)
+{
+ struct ast_flags flags = { 0 };
+ struct ast_config *config = ast_config_load2("sorcery.conf", "sorcery", flags);
+ struct ast_variable *mapping;
+ int res = 0;
+
+ if (!config || (config == CONFIG_STATUS_FILEMISSING) || (config = CONFIG_STATUS_FILEINVALID)) {
+ return -1;
+ }
+
+ for (mapping = ast_variable_browse(config, name); mapping; mapping = mapping->next) {
+ char *data = ast_strdupa(mapping->value), *wizard = strsep(&data, ",");
+
+ /* If no wizard exists just skip, nothing we can do */
+ if (ast_strlen_zero(wizard)) {
+ continue;
+ }
+
+ /* Any error immediately causes us to stop */
+ if ((res = sorcery_apply_wizard_mapping(sorcery, mapping->name, wizard, data))) {
+ break;
+ }
+ }
+
+ ast_config_destroy(config);
+
+ return 0;
+}
int ast_sorcery_apply_default(struct ast_sorcery *sorcery, const char *type, const char *name, const char *data)
{
- struct ast_sorcery_object_type *object_type = ao2_find(sorcery->types, type, OBJ_KEY);
-
- /* Defaults can not overrule any existing wizards */
+ RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+
+ /* Defaults can not be added if any existing mapping exists */
if (object_type) {
- ao2_ref(object_type, -1);
- return -1;
- }
+ return -1;
+ }
+
+ return sorcery_apply_wizard_mapping(sorcery, type, name, data);
+}
+
+int ast_sorcery_object_register(struct ast_sorcery *sorcery, const char *type, struct aco_type *aco_type)
+{
+ RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+
+ if (!object_type) {
+ return -1;
+ }
+
+ object_type->file->types[0] = aco_type;
+ object_type->file->types[1] = NULL;
+
+ if (aco_info_init(object_type->info)) {
+ return -1;
+ }
+
+ object_type->type = aco_type;
+
+ return 0;
+}
+
+int __ast_sorcery_object_field_register(struct ast_sorcery *sorcery, const char *type, const char *name, const char *default_val, enum aco_option_type opt_type,
+ aco_option_handler config_handler, sorcery_field_handler sorcery_handler, unsigned int flags, size_t argc, ...)
+{
+ RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+ va_list args;
+
+ if (!object_type) {
+ return -1;
+ }
+
+ if (!sorcery_handler) {
+ sorcery_handler = sorcery_field_default_handler(opt_type);
+ }
+
+ if (sorcery_handler) {
+ RAII_VAR(struct ast_sorcery_object_field *, object_field, NULL, ao2_cleanup);
+ int pos;
+
+ if (!(object_field = ao2_alloc(sizeof(*object_field) + argc * sizeof(object_field->args[0]), NULL))) {
+ return -1;
+ }
+
+ ast_copy_string(object_field->name, name, sizeof(object_field->name));
+ object_field->handler = sorcery_handler;
+
+ va_start(args, argc);
+ for (pos = 0; pos < argc; pos++) {
+ object_field->args[pos] = va_arg(args, size_t);
+ }
+ va_end(args);
+
+ ao2_link(object_type->fields, object_field);
+ }
+
+ va_start(args, argc);
+ /* TODO: Improve this hack */
+ if (!argc) {
+ __aco_option_register(object_type->info, name, ACO_EXACT, object_type->file->types, default_val, opt_type, config_handler, flags, argc);
+ } else if (argc == 1) {
+ __aco_option_register(object_type->info, name, ACO_EXACT, object_type->file->types, default_val, opt_type, config_handler, flags, argc,
+ va_arg(args, size_t));
+ } else if (argc == 2) {
+ __aco_option_register(object_type->info, name, ACO_EXACT, object_type->file->types, default_val, opt_type, config_handler, flags, argc,
+ va_arg(args, size_t), va_arg(args, size_t));
+ } else if (argc == 3) {
+ __aco_option_register(object_type->info, name, ACO_EXACT, object_type->file->types, default_val, opt_type, config_handler, flags, argc,
+ va_arg(args, size_t), va_arg(args, size_t), va_arg(args, size_t));
+ }
+ va_end(args);
return 0;
}
@@ -208,6 +519,117 @@
void ast_sorcery_ref(struct ast_sorcery *sorcery)
{
ao2_ref(sorcery, +1);
+}
+
+struct ast_variable *ast_sorcery_objectset_create(const struct ast_sorcery *sorcery, const void *object)
+{
+ const struct ast_sorcery_object_details *details = object;
+ RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->type, OBJ_KEY), ao2_cleanup);
+ struct ao2_iterator i;
+ struct ast_sorcery_object_field *object_field;
+ struct ast_variable *fields = NULL;
+ int res = 0;
+
+ if (!object_type) {
+ return NULL;
+ }
+
+ i = ao2_iterator_init(object_type->fields, 0);
+
+ for (; (object_field = ao2_iterator_next(&i)); ao2_ref(object_field, -1)) {
+ char buf[512];
+ struct ast_variable *tmp;
+
+ /* Any fields with no handler just get skipped */
+ if (!object_field->handler) {
+ continue;
+ }
+
+ if ((res = object_field->handler(object, object_field->args, buf, sizeof(buf))) ||
+ !(tmp = ast_variable_new(object_field->name, buf, ""))) {
+ res = -1;
+ ao2_ref(object_field, -1);
+ break;
+ }
+
+ tmp->next = fields;
+ fields = tmp;
+ }
+
+ ao2_iterator_destroy(&i);
+
+ /* If any error occurs we destroy all fields handled before so a partial objectset is not returned */
+ if (res) {
+ ast_variables_destroy(fields);
+ fields = NULL;
+ }
+
+ return fields;
+}
+
+int ast_sorcery_objectset_apply(const struct ast_sorcery *sorcery, void *object, struct ast_variable *objectset)
+{
+ const struct ast_sorcery_object_details *details = object;
+ RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->type, OBJ_KEY), ao2_cleanup);
+ struct ast_variable *field;
+ int res = 0;
+
+ if (!object_type) {
+ return -1;
+ }
+
+ for (field = objectset; field; field = field->next) {
+ if ((res = aco_process_var(object_type->type, details->id, field, object))) {
+ break;
+ }
+ }
+
+ return res;
+}
+
+static const struct ast_variable *sorcery_find_field(const struct ast_variable *fields, const char *name)
+{
+ const struct ast_variable *field;
+
+ /* Search the linked list of fields to find the correct one */
+ for (field = fields; field; field = field->next) {
+ if (!strcmp(field->name, name)) {
+ return field;
+ }
+ }
+
+ return NULL;
+}
+
+struct ast_variable *ast_sorcery_changeset_create(const struct ast_variable *original, const struct ast_variable *modified)
+{
+ const struct ast_variable *field;
+ struct ast_variable *changes = NULL;
+ int res = 0;
+
+ for (field = modified; field; field = field->next) {
+ const struct ast_variable *old = sorcery_find_field(original, field->name);
+
+ if (!old || strcmp(old->value, field->value)) {
+ struct ast_variable *tmp;
+
+ if (!(tmp = ast_variable_new(field->name, field->value, ""))) {
+ res = -1;
+ break;
+ }
+
+ tmp->next = changes;
+ changes = tmp;
+ }
+ }
+
+ /* If an error occurred do not return a partial changeset */
+ if (res) {
+ ast_variables_destroy(changes);
+ changes = NULL;
+ }
+
+ return changes;
}
void *ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, const char *id)
@@ -216,6 +638,7 @@
struct ast_sorcery_object_details *details;
if (!object_type || !object_type->type ||
+ !object_type->type->item_alloc ||
!(details = object_type->type->item_alloc(id))) {
return NULL;
}
@@ -231,10 +654,43 @@
return details;
}
+void *ast_sorcery_copy(const struct ast_sorcery *sorcery, const void *object)
+{
+ const struct ast_sorcery_object_details *details = object;
+ void *copy = ast_sorcery_alloc(sorcery, details->type, details->id);
+ RAII_VAR(struct ast_variable *, objectset, NULL, ast_variables_destroy);
+
+ if (!copy ||
+ !(objectset = ast_sorcery_objectset_create(sorcery, object)) ||
+ ast_sorcery_objectset_apply(sorcery, copy, objectset)) {
+ ao2_cleanup(copy);
+ return NULL;
+ }
+
+ return copy;
+}
+
+struct ast_variable *ast_sorcery_diff(const struct ast_sorcery *sorcery, const void *original, const void *modified)
+{
+ RAII_VAR(struct ast_variable *, objectset1, NULL, ast_variables_destroy);
+ RAII_VAR(struct ast_variable *, objectset2, NULL, ast_variables_destroy);
+
+ if (strcmp(ast_sorcery_object_get_type(original), ast_sorcery_object_get_type(modified))) {
+ return NULL;
+ }
+
+ objectset1 = ast_sorcery_objectset_create(sorcery, original);
+ objectset2 = ast_sorcery_objectset_create(sorcery, modified);
+
+ return ast_sorcery_changeset_create(objectset1, objectset2);
+}
+
/*! \brief Internal function which returns if the wizard has created the object */
static int sorcery_wizard_create(void *obj, void *arg, int flags)
{
- return CMP_MATCH | CMP_STOP;
+ struct ast_sorcery_object_wizard *object_wizard = obj;
+
+ return !object_wizard->wizard->create(object_wizard->data, arg) ? CMP_MATCH | CMP_STOP : 0;
}
int ast_sorcery_create(struct ast_sorcery *sorcery, void *object)
@@ -249,54 +705,46 @@
if ((object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_create, object))) {
/* Object was successfully created by the wizard so update the object details to reflect it */
- details->wizard = object_wizard->wizard;
+ details->wizard = object_wizard;
return 0;
} else {
return -1;
}
}
-/*! \brief Internal function which returns the wizard responsible for the object */
-static int sorcery_wizard_match(void *obj, void *arg, int flags)
-{
- struct ast_sorcery_object_wizard *wizard = obj;
- struct ast_sorcery_object_details *details = arg;
-
- return (wizard->wizard == details->wizard) ? CMP_MATCH | CMP_STOP : 0;
-}
-
int ast_sorcery_update(struct ast_sorcery *sorcery, void *object)
{
- return -1;
+ struct ast_sorcery_object_details *details = object;
+
+ ast_assert(sorcery != NULL);
+ ast_assert(details->wizard != NULL);
+
+ return details->wizard->wizard->update(details->wizard->data, object);
}
int ast_sorcery_delete(struct ast_sorcery *sorcery, void *object)
{
struct ast_sorcery_object_details *details = object;
- RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->type, OBJ_KEY), ao2_cleanup);
- RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, NULL, ao2_cleanup);
-
- if (!object_type ||
- !(object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_match, details))) {
- return -1;
- }
-
- return -1;
+
+ ast_assert(sorcery != NULL);
+ ast_assert(details->wizard != NULL);
+
+ return details->wizard->wizard->delete(details->wizard->data, object);
}
void ast_sorcery_unref(struct ast_sorcery *sorcery)
{
- ao2_ref(sorcery, -1);
-}
-
-const char *ast_sorcery_object_get_id(void *object)
-{
- struct ast_sorcery_object_details *details = object;
+ ao2_cleanup(sorcery);
+}
+
+const char *ast_sorcery_object_get_id(const void *object)
+{
+ const struct ast_sorcery_object_details *details = object;
return details->id;
}
-const char *ast_sorcery_object_get_type(void *object)
-{
- struct ast_sorcery_object_details *details = object;
+const char *ast_sorcery_object_get_type(const void *object)
+{
+ const struct ast_sorcery_object_details *details = object;
return details->type;
}
Added: team/file/sorcery/res/res_sorcery_memory.c
URL: http://svnview.digium.com/svn/asterisk/team/file/sorcery/res/res_sorcery_memory.c?view=auto&rev=378071
==============================================================================
--- team/file/sorcery/res/res_sorcery_memory.c (added)
+++ team/file/sorcery/res/res_sorcery_memory.c Mon Dec 17 10:03:45 2012
@@ -1,0 +1,119 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2012, Digium, Inc.
+ *
+ * Joshua Colp <jcolp at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Sorcery In-Memory Object Wizard
+ *
+ * \author Joshua Colp <jcolp at digium.com>
+ */
+
+/*** MODULEINFO
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/sorcery.h"
+#include "asterisk/astobj2.h"
+
+/*! \brief Number of buckets for sorcery objects */
+#define OBJECT_BUCKETS 53
+
+static void *sorcery_memory_open(const char *data);
+static int sorcery_memory_create(void *data, void *object);
+static int sorcery_memory_update(void *data, void *object);
+static int sorcery_memory_delete(void *data, void *object);
+static void sorcery_memory_close(void *data);
+
+static struct ast_sorcery_wizard memory_object_wizard = {
+ .name = "memory",
+ .open = sorcery_memory_open,
+ .create = sorcery_memory_create,
+ .update = sorcery_memory_update,
+ .delete = sorcery_memory_delete,
+ .close = sorcery_memory_close,
+};
+
+/*! \brief Hashing function for sorcery objects */
+static int sorcery_memory_hash(const void *obj, const int flags)
+{
+ const char *id = obj;
+
+ return ast_str_hash(flags & OBJ_KEY ? id : ast_sorcery_object_get_id(obj));
+}
+
+/*! \brief Comparator function for sorcery objects */
+static int sorcery_memory_cmp(void *obj, void *arg, int flags)
+{
+ const char *id = arg;
+
+ return !strcmp(ast_sorcery_object_get_id(obj), flags & OBJ_KEY ? id : ast_sorcery_object_get_id(arg)) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static int sorcery_memory_create(void *data, void *object)
+{
+ ao2_link(data, object);
+ return 0;
+}
+
+static int sorcery_memory_update(void *data, void *object)
+{
+ return -1;
+}
+
+static int sorcery_memory_delete(void *data, void *object)
+{
+ ao2_unlink(data, object);
+ return 0;
+}
+
+static void *sorcery_memory_open(const char *data)
+{
+ return ao2_container_alloc(OBJECT_BUCKETS, sorcery_memory_hash, sorcery_memory_cmp);
+}
+
+static void sorcery_memory_close(void *data)
+{
+ ao2_ref(data, -1);
+}
+
+static int load_module(void)
+{
+ if (ast_sorcery_wizard_register(&memory_object_wizard, NULL)) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ ast_sorcery_wizard_unregister(&memory_object_wizard);
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Sorcery In-Memory Object Wizard",
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+);
Propchange: team/file/sorcery/res/res_sorcery_memory.c
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: team/file/sorcery/res/res_sorcery_memory.c
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: team/file/sorcery/res/res_sorcery_memory.c
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: team/file/sorcery/tests/test_sorcery.c
URL: http://svnview.digium.com/svn/asterisk/team/file/sorcery/tests/test_sorcery.c?view=diff&rev=378071&r1=378070&r2=378071
==============================================================================
--- team/file/sorcery/tests/test_sorcery.c (original)
+++ team/file/sorcery/tests/test_sorcery.c Mon Dec 17 10:03:45 2012
@@ -38,6 +38,27 @@
#include "asterisk/sorcery.h"
#include "asterisk/logger.h"
+/*! \brief Dummy sorcery object */
+struct test_sorcery_object {
+ SORCERY_OBJECT(details);
+ unsigned int bob;
+ unsigned int joe;
+};
+
+/*! \brief Internal function to allocate a test object */
+static void *test_sorcery_object_alloc(const char *id)
+{
+ return ao2_alloc(sizeof(struct test_sorcery_object), NULL);
+}
+
+/*! \brief Configuration framework definition for test object */
+static struct aco_type test_object = {
+ .type = ACO_ITEM,
+ .category_match = ACO_BLACKLIST,
+ .category = "^general$",
+ .item_alloc = test_sorcery_object_alloc,
+};
+
/*! \brief Dummy sorcery wizard, not actually used so we only populate the name and nothing else */
static struct ast_sorcery_wizard test_wizard = {
.name = "test",
@@ -82,42 +103,636 @@
AST_TEST_DEFINE(open)
{
+ RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "open";
+ info->category = "/main/sorcery/";
+ info->summary = "sorcery open unit test";
+ info->description =
+ "Test opening of sorcery";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ if (!(sorcery = ast_sorcery_open())) {
+ ast_test_status_update(test, "Failed to open new sorcery structure\n");
+ return AST_TEST_FAIL;
+ }
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(apply_default)
+{
+ RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "apply_default";
+ info->category = "/main/sorcery/";
+ info->summary = "sorcery default wizard unit test";
+ info->description =
+ "Test setting default type wizard in sorcery";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ if (!(sorcery = ast_sorcery_open())) {
+ ast_test_status_update(test, "Failed to open sorcery with NULL name\n");
+ return AST_TEST_FAIL;
+ }
+
+ if (!ast_sorcery_apply_default(sorcery, "test", "dummy", NULL)) {
+ ast_test_status_update(test, "Successfully set a default wizard that doesn't exist\n");
+ return AST_TEST_FAIL;
+ }
+
+ if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
+ ast_test_status_update(test, "Failed to set a known wizard as a default\n");
+ return AST_TEST_FAIL;
+ }
+
+ if (!ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
+ ast_test_status_update(test, "Successfully set a default wizard on a type twice\n");
+ return AST_TEST_FAIL;
+ }
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(object_register)
+{
+ RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "object_register";
+ info->category = "/main/sorcery/";
+ info->summary = "sorcery object type registration unit test";
+ info->description =
+ "Test object type registration in sorcery";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ if (!(sorcery = ast_sorcery_open())) {
+ ast_test_status_update(test, "Failed to open sorcery with NULL name\n");
+ return AST_TEST_FAIL;
+ }
+
+ if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
+ ast_test_status_update(test, "Failed to set a known wizard as a default\n");
+ return AST_TEST_FAIL;
+ }
+
+ if (ast_sorcery_object_register(sorcery, "test", &test_object)) {
+ ast_test_status_update(test, "Failed to register object type\n");
+ return AST_TEST_FAIL;
+ }
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(object_alloc)
+{
int res = AST_TEST_PASS;
- struct ast_sorcery *sorcery;
-
- switch (cmd) {
- case TEST_INIT:
- info->name = "open";
- info->category = "/main/sorcery/";
- info->summary = "sorcery open unit test";
- info->description =
- "Test opening of sorcery";
- return AST_TEST_NOT_RUN;
- case TEST_EXECUTE:
- break;
- }
-
- if (!(sorcery = ast_sorcery_open(NULL))) {
+ RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+ RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "object_alloc";
+ info->category = "/main/sorcery/";
+ info->summary = "sorcery object allocation unit test";
+ info->description =
+ "Test object allocation in sorcery";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ if (!(sorcery = ast_sorcery_open())) {
ast_test_status_update(test, "Failed to open sorcery with NULL name\n");
- res = AST_TEST_FAIL;
- }
-
- ast_sorcery_unref(sorcery);
-
- if (!(sorcery = ast_sorcery_open("test"))) {
- ast_test_status_update(test, "Failed to open sorcery with populated name\n");
- res = AST_TEST_FAIL;
- }
-
- ast_sorcery_unref(sorcery);
+ return AST_TEST_FAIL;
+ }
+
+ if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
+ ast_test_status_update(test, "Failed to set a known wizard as a default\n");
+ return AST_TEST_FAIL;
+ }
+
+ if (ast_sorcery_object_register(sorcery, "test", &test_object)) {
+ ast_test_status_update(test, "Failed to register object type\n");
+ return AST_TEST_FAIL;
+ }
+
+ ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct test_sorcery_object, bob));
+ ast_sorcery_object_field_register(sorcery, "test", "joe", "10", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct test_sorcery_object, joe));
+
+ if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
+ ast_test_status_update(test, "Failed to allocate a known object type\n");
+ res = AST_TEST_FAIL;
+ } else if (ast_strlen_zero(ast_sorcery_object_get_id(obj))) {
+ ast_test_status_update(test, "Allocated object has empty id when it should not\n");
+ res = AST_TEST_FAIL;
+ } else if (strcmp(ast_sorcery_object_get_id(obj), "blah")) {
+ ast_test_status_update(test, "Allocated object does not have correct id\n");
+ res = AST_TEST_FAIL;
+ } else if (obj->details.wizard) {
+ ast_test_status_update(test, "Allocated object has wizard when it should not\n");
+ res = AST_TEST_FAIL;
+ } else if ((obj->bob != 5) || (obj->joe != 10)) {
+ ast_test_status_update(test, "Allocated object does not have defaults set as it should\n");
+ res = AST_TEST_FAIL;
+ }
return res;
+}
+
+AST_TEST_DEFINE(object_copy)
+{
+ int res = AST_TEST_PASS;
+ RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+ RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
+ RAII_VAR(struct test_sorcery_object *, copy, NULL, ao2_cleanup);
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "object_copy";
+ info->category = "/main/sorcery/";
+ info->summary = "sorcery object copy unit test";
+ info->description =
+ "Test object copy in sorcery";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ if (!(sorcery = ast_sorcery_open())) {
+ ast_test_status_update(test, "Failed to open sorcery with NULL name\n");
+ return AST_TEST_FAIL;
+ }
+
+ if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
+ ast_test_status_update(test, "Failed to set a known wizard as a default\n");
+ return AST_TEST_FAIL;
+ }
+
+ if (ast_sorcery_object_register(sorcery, "test", &test_object)) {
+ ast_test_status_update(test, "Failed to register object type\n");
+ return AST_TEST_FAIL;
+ }
+
+ ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct test_sorcery_object, bob));
+ ast_sorcery_object_field_register(sorcery, "test", "joe", "10", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct test_sorcery_object, joe));
+
+ if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
+ ast_test_status_update(test, "Failed to allocate a known object type\n");
[... 462 lines stripped ...]
More information about the asterisk-commits
mailing list