[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