grim/gplate

I thought I already added this.. my bad
trying_to_get_includes_working
2008-02-20, grim
545b7214a0de
I thought I already added this.. my bad
/*
* Copyright (C) 2007-2008 Gary Kramlich <grim@reaperworld.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <gplate/gplate-library.h>
#include <gplate/gplate-errors.h>
/******************************************************************************
* Structs
*****************************************************************************/
typedef struct {
GType function;
GType tag;
} GPlateFunctionData;
typedef struct {
GType tag;
gchar *prefix;
gchar *suffix;
GType default_function;
} GPlateTagData;
/******************************************************************************
* Globals
*****************************************************************************/
static GHashTable *functions = NULL;
static GHashTable *tags = NULL;
static GType default_tag = G_TYPE_INVALID;
/******************************************************************************
* GPlateFunctionData API
*****************************************************************************/
static GPlateFunctionData *
gplate_function_data_new(GType function, GType tag) {
GPlateFunctionData *data = g_new(GPlateFunctionData, 1);
data->function = function;
data->tag = tag;
return data;
}
static void
gplate_function_data_free(GPlateFunctionData *data) {
g_free(data);
}
/******************************************************************************
* GPlateTagData API
*****************************************************************************/
static GPlateTagData *
gplate_tag_data_new(GType tag, GError **error) {
GPlateTagData *data = NULL;
GPlateTagClass *klass = NULL;
const gchar *prefix = NULL, *suffix = NULL;
klass = g_type_class_ref(tag);
prefix = gplate_tag_class_get_prefix(klass);
suffix = gplate_tag_class_get_suffix(klass);
if(!prefix && suffix) {
if(error) {
*error =
g_error_new(GPLATE_DOMAIN,
GPLATE_ERROR_LIBRARY_TAG_NO_PREFIX_HAVE_SUFFIX,
"tag '%s' does not have a prefix, but has a "
"suffix.",
g_type_name(tag));
}
return NULL;
} else if(prefix && !suffix) {
if(error) {
*error =
g_error_new(GPLATE_DOMAIN,
GPLATE_ERROR_LIBRARY_TAG_HAVE_PREFIX_NO_SUFFIX,
"tag '%s' has a prefix, but does not have a "
"suffix.",
g_type_name(tag));
}
return NULL;
}
if(!prefix && !suffix) {
if(default_tag != G_TYPE_INVALID) {
if(error) {
*error =
g_error_new(GPLATE_DOMAIN,
GPLATE_ERROR_LIBRARY_DEFAULT_TAG_REGISTERED,
"A default tag named '%s' has already been "
"registered.",
g_type_name(default_tag));
}
return NULL;
} else {
default_tag = tag;
}
}
data = g_new(GPlateTagData, 1);
data->tag = tag;
data->prefix = (prefix) ? g_strdup(prefix) : NULL;
data->suffix = (suffix) ? g_strdup(suffix) : NULL;
data->default_function = G_TYPE_INVALID;
g_type_class_unref(klass);
return data;
}
static void
gplate_tag_data_free(GPlateTagData *data) {
g_free(data->prefix);
g_free(data->suffix);
g_free(data);
}
/******************************************************************************
* Inlines
*****************************************************************************/
static inline void
gplate_library_lazy_init(void) {
if(G_UNLIKELY(functions == NULL)) {
functions =
g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
(GDestroyNotify)gplate_function_data_free);
}
if(G_UNLIKELY(tags == NULL)) {
tags = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
(GDestroyNotify)gplate_tag_data_free);
}
}
static inline gboolean
gplate_library_check_type(GType type, GType parent, GError **error) {
if(!g_type_is_a(type, parent)) {
if(error) {
*error = g_error_new(GPLATE_DOMAIN,
GPLATE_ERROR_TYPE_IS_NOT_DESCENDANT,
"%s is not a subclass of %s.",
g_type_name(type),
g_type_name(parent));
}
return FALSE;
}
if(G_TYPE_IS_ABSTRACT(type)) {
if(error) {
*error = g_error_new(GPLATE_DOMAIN,
GPLATE_ERROR_TYPE_IS_ABSTRACT,
"%s is abstract.",
g_type_name(type));
}
return FALSE;
}
return TRUE;
}
/******************************************************************************
* GPlateLibrary API
*****************************************************************************/
/**
* gplate_library_add_function:
* @name: The name of the function.
* @function: The #GType of the function.
* @error: The return address for errors.
*
* Adds @function to the library.
*
* Return Value: TRUE on success, otherwise FALSE with @error set.
*/
gboolean
gplate_library_add_function(const gchar *name, GType function, GError **error)
{
GPlateFunctionData *data = NULL;
g_return_val_if_fail(name, FALSE);
gplate_library_lazy_init();
if(!gplate_library_check_type(function, GPLATE_TYPE_FUNCTION, error))
return FALSE;
data = g_hash_table_lookup(functions, name);
if(data) {
/* if the function is already registered with the same name and
* function, return TRUE and jump out.
*/
if(data->function == function)
return TRUE;
if(error) {
*error = g_error_new(GPLATE_DOMAIN,
GPLATE_ERROR_LIBRARY_FUNCTION_NAME_EXISTS,
"Function name '%s' is already registered "
"to '%s'",
name, g_type_name(data->function));
}
return FALSE;
}
data = gplate_function_data_new(function, G_TYPE_INVALID);
g_hash_table_insert(functions, g_strdup(name), data);
return TRUE;
}
/**
* gplate_library_remove_function:
* @name: The name of the function to remove.
* @error: Return address for errors.
*
* Removes a function from the library by it's name.
*
* Return Value: TRUE if the function was removed, otherwise FALSE.
*/
gboolean
gplate_library_remove_function(const gchar *name, GError **error) {
gboolean ret;
g_return_val_if_fail(name, FALSE);
ret = g_hash_table_remove(functions, name);
if(!ret && error) {
*error = g_error_new(GPLATE_DOMAIN,
GPLATE_ERROR_LIBRARY_FUNCTION_NAME_NOT_FOUND,
"Function '%s' was not found.",
name);
}
return ret;
}
/**
* gplate_library_add_tag:
* @tag: The #GPlateTag's GType to add.
* @error: The return address for any errors.
*
* Adds a tag to the library.
*
* Tags must be added to the library via this function for a template to
* handle them.
*
* Return Value: TRUE on success, FALSE on failure with @error set.
*/
gboolean
gplate_library_add_tag(GType tag, GError **error) {
GPlateTagData *data = NULL;
gplate_library_lazy_init();
if(!gplate_library_check_type(tag, GPLATE_TYPE_TAG, error))
return FALSE;
data = g_hash_table_lookup(tags, GSIZE_TO_POINTER(tag));
if(data) {
if(error) {
*error = g_error_new(GPLATE_DOMAIN,
GPLATE_ERROR_LIBRARY_TAG_EXISTS,
"Tag '%s' is already in the library",
g_type_name(tag));
}
return FALSE;
}
data = gplate_tag_data_new(tag, error);
if(!data)
return FALSE;
g_hash_table_insert(tags, GSIZE_TO_POINTER(data->tag), data);
return TRUE;
}
/**
* gplate_library_remove_tag:
* @tag: The #GType of the tag to remove from the library.
* @error: The return address for any errors.
*
* Unregisters @tag from the library.
*
* Return Value: TRUE on success, FALSE on failure with @error set.
*/
gboolean
gplate_library_remove_tag(GType tag, GError **error) {
gpointer data;
gplate_library_lazy_init();
if(!gplate_library_check_type(tag, GPLATE_TYPE_TAG, error))
return FALSE;
data = g_hash_table_lookup(tags, GSIZE_TO_POINTER(tag));
if(!data) {
if(error) {
*error = g_error_new(GPLATE_DOMAIN,
GPLATE_ERROR_LIBRARY_TAG_NOT_FOUND,
"Tag '%s' is not in the library.",
g_type_name(tag));
}
return FALSE;
}
g_hash_table_remove(tags, GSIZE_TO_POINTER(tag));
return TRUE;
}
/**
* gplate_library_bind_function:
* @name: The name of the function.
* @tag: The #GType of the tag.
* @error: The return address for errors.
*
* Binds the already registered function @name to @tag.
*
* Return Value: TRUE on success, FALSE on failure with @error set.
*/
gboolean
gplate_library_bind_function(const gchar *name, GType tag, GError **error) {
GPlateFunctionData *data = NULL;
gplate_library_lazy_init();
g_return_val_if_fail(name, FALSE);
if(!gplate_library_check_type(tag, GPLATE_TYPE_TAG, error))
return FALSE;
data = g_hash_table_lookup(functions, name);
if(!data) {
if(error) {
*error = g_error_new(GPLATE_DOMAIN,
GPLATE_ERROR_LIBRARY_FUNCTION_NAME_NOT_FOUND,
"There is no function registered with the "
"name '%s'",
name);
}
return FALSE;
}
data->tag = tag;
return TRUE;
}
/**
* gplate_library_unbind_function:
* @name: The name of the function to unbind.
* @error: The return address for any errors.
*
* Unbinds @name.
*
* If @name is not already bound, this will return FALSE with a
* GPLATE_ERROR_LIBRARY_FUNCTION_NOT_BOUND error.
*
* Return Value: TRUE on success, FALSE on failure with @error set.
*/
gboolean
gplate_library_unbind_function(const gchar *name, GError **error) {
GPlateFunctionData *data = NULL;
g_return_val_if_fail(name, FALSE);
gplate_library_lazy_init();
data = g_hash_table_lookup(functions, name);
if(!data) {
if(error) {
*error = g_error_new(GPLATE_DOMAIN,
GPLATE_ERROR_LIBRARY_FUNCTION_NAME_NOT_FOUND,
"No function named '%s' is registered",
name);
}
return FALSE;
}
if(data->tag == G_TYPE_INVALID) {
if(error) {
*error = g_error_new(GPLATE_DOMAIN,
GPLATE_ERROR_LIBRARY_FUNCTION_NOT_BOUND,
"Function '%s' is not bound to a tag",
name);
}
return FALSE;
}
data->tag = G_TYPE_INVALID;
return TRUE;
}
/**
* gplate_library_add_bound_function:
* @name: The name of the function.
* @function: The #GType of the function.
* @tag: The #GType of the tag.
* @error: The return address for any errors.
*
* Registers @function with @name and then binds it to @tag.
*
* This is just a wrapper around #gplate_library_add_function and
* #gplate_library_bind_function.
*
* Return Value: TRUE on success, otherwise FALSE with @error set.
*/
gboolean
gplate_library_add_bound_function(const gchar *name, GType function, GType tag,
GError **error)
{
gboolean ret;
g_return_val_if_fail(name, FALSE);
gplate_library_lazy_init();
ret = gplate_library_add_function(name, function, error);
if(!ret)
return FALSE;
ret = gplate_library_bind_function(name, tag, error);
return ret;
}
/**
* gplate_library_lookup_function:
* @name: The name of the function to find.
* @function: Return address for the #GType of the functions.
* @tag: Return address for the #GType of the associated tag.
* @error: Return address for any errors.
*
* Looks for a function in the library.
*
* If the function is found, @function and @type will be set apporiately.
*
* If the function is not found, @function and @type are undefined, and error
* will be set.
*
* Return Value: TRUE if @name is found in the library, otherwise FALSE.
*/
gboolean
gplate_library_lookup_function(const gchar *name, GType *function, GType *tag,
GError **error)
{
GPlateFunctionData *data = NULL;
g_return_val_if_fail(name, FALSE);
data = g_hash_table_lookup(functions, name);
if(!data) {
if(error) {
*error = g_error_new(GPLATE_DOMAIN,
GPLATE_ERROR_LIBRARY_FUNCTION_NAME_NOT_FOUND,
"Function '%s' was not found.",
name);
}
return FALSE;
}
if(function)
*function = data->function;
if(tag)
*tag = data->tag;
return TRUE;
}
/**
* gplate_library_lookup_function_for_tag:
* @name: The name of the function to lookup.
* @tag: The #GType of the tag.
* @error: The return address for any error.
*
* Looks up a function named @name that is bound to @tag.
*
* Return Value: The #GType of the function on success, otherwise
* #G_TYPE_INVALID on failure with @error set.
*/
GType
gplate_library_lookup_function_for_tag(const gchar *name, GType tag,
GError **error)
{
GType type = G_TYPE_INVALID, function = G_TYPE_INVALID;
gplate_library_lazy_init();
if(!gplate_library_check_type(tag, GPLATE_TYPE_TAG, error))
return G_TYPE_INVALID;
if(!gplate_library_lookup_function(name, &function, &type, error))
return G_TYPE_INVALID;
if(type != tag) {
if(error) {
*error = g_error_new(GPLATE_DOMAIN,
GPLATE_ERROR_LIBRARY_FUNCTION_NOT_BOUND_TO_TAG,
"Function '%s' is not bound to '%s'",
name,
g_type_name(tag));
}
return G_TYPE_INVALID;
}
return function;
}
/**
* gplate_library_lookup_tag:
* @tag: The GType of the tag to lookup.
* @prefix: The return address for the prefix.
* @suffix: The return address for the suffix.
* @error: The return address for any errors.
*
* Looks for @tag in the library. If the tag is found, TRUE is returned and
* @prefix and @suffix will be set. NULL can be passed for @prefix and
* @suffix. If @prefix and @suffix are non-NULL they must be #g_free'd.
*
* If @tag is not found in the library, FALSE will be returned and @error will
* be set.
*
* Return Value: TRUE on success, FALSE otherwise.
*/
gboolean
gplate_library_lookup_tag(GType tag, gchar **prefix, gchar **suffix,
GError **error)
{
GPlateTagData *data = NULL;
gplate_library_lazy_init();
data = g_hash_table_lookup(tags, GSIZE_TO_POINTER(tag));
if(!data) {
if(error) {
*error = g_error_new(GPLATE_DOMAIN,
GPLATE_ERROR_LIBRARY_TAG_NOT_FOUND,
"Tag '%s' was not found in the library",
g_type_name(tag));
}
return FALSE;
}
if(prefix)
*prefix = (data->prefix) ? g_strdup(data->prefix) : NULL;
if(suffix)
*suffix = (data->suffix) ? g_strdup(data->suffix) : NULL;
return TRUE;
}
/**
* gplate_library_get_default_tag:
*
* Gets the default tag if one has been registered.
*
* A default tag is one that does not have a prefix or a suffix. It is used
* when no other tags match.
*
* Return Value: The default tag if one has been registered, otherwise
* #G_TYPE_INVALID.
*/
GType
gplate_library_get_default_tag(void) {
return default_tag;
}
/**
* gplate_library_set_default_function_for_tag:
* @name: The name of the function.
* @tag: The #GType of the tag.
* @error: The return address for any errors.
*
* Sets the default function of @tag to @name.
*
* A default function is used when the contents of a tag do not match any
* functions that are registered to the tag.
*
* For example, the default function of #GPlateCommentTag is
* #GPlateNoopFunction.
*
* Return Value: TRUE on success, otherwise FALSE with @error set.
*/
gboolean
gplate_library_set_default_function_for_tag(const gchar *name, GType tag,
GError **error)
{
GPlateTagData *tdata = NULL;
GType function = G_TYPE_INVALID;
g_return_val_if_fail(name, FALSE);
gplate_library_lazy_init();
tdata = g_hash_table_lookup(tags, GSIZE_TO_POINTER(tag));
if(!tdata) {
if(error) {
*error = g_error_new(GPLATE_DOMAIN,
GPLATE_ERROR_LIBRARY_TAG_NOT_FOUND,
"tag '%s' is not registered",
g_type_name(tag));
}
return FALSE;
}
if(!gplate_library_lookup_function(name, &function, NULL, error))
return FALSE;
tdata->default_function = function;
return TRUE;
}
/**
* gplate_library_get_default_function_for_tag:
* @tag: The #GPlateTag.
* @error: The return address for any errors.
*
* Get's the default function for @tag.
*
* Return Value: On success, the #GType of the function is returned.
* On failure, #G_TYPE_INVALID is returned and @error is set.
*
* However, if a tag does not have a default function,
* #G_TYPE_INVALID will be returned. So it is recommended to
* pass @error and check if it's set before assuming a return
* value of #G_TYPE_INVALID is an error.
*/
GType
gplate_library_get_default_function_for_tag(GType tag, GError **error) {
GPlateTagData *tdata = NULL;
gplate_library_lazy_init();
tdata = g_hash_table_lookup(tags, GSIZE_TO_POINTER(tag));
if(!tdata) {
if(error) {
*error = g_error_new(GPLATE_DOMAIN,
GPLATE_ERROR_LIBRARY_TAG_NOT_FOUND,
"tag '%s' is not registered",
g_type_name(tag));
}
return G_TYPE_INVALID;
}
return tdata->default_function;
}
/******************************************************************************
* For each stuff
*****************************************************************************/
typedef struct {
GPlateLibraryTagsForeachFunc func;
gpointer data;
} GPlateLibraryTagsForeachData;
static void
gplate_library_tags_foreach_helper(gpointer k, gpointer v, gpointer d) {
GPlateTagData *data = (GPlateTagData *)v;
GPlateLibraryTagsForeachData *fhd = (GPlateLibraryTagsForeachData *)d;
fhd->func(data->tag, data->prefix, data->suffix, fhd->data);
}
/**
* gplate_library_tags_foreach:
* @func: The callback function.
* @data: User data to pass to the callback function.
*
* Calls @func with each tag registered in the library.
*/
void
gplate_library_tags_foreach(GPlateLibraryTagsForeachFunc func, gpointer data) {
GPlateLibraryTagsForeachData d = { 0, };
gplate_library_lazy_init();
d.func = func;
d.data = data;
g_hash_table_foreach(tags, gplate_library_tags_foreach_helper, &d);
}