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-template.h>
#include <gplate/gplate-collection.h>
#include <gplate/gplate-dictionary-variable.h>
#include <gplate/gplate-errors.h>
#include <gplate/gplate-library.h>
#include <gplate/gplate-util.h>
#include <stdio.h>
#include <string.h>
#define GPLATE_TEMPLATE_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE((obj), GPLATE_TYPE_TEMPLATE, GPlateTemplatePrivate))
/******************************************************************************
* Enums
*****************************************************************************/
/******************************************************************************
* Structs
*****************************************************************************/
typedef struct {
GPlateVariable *vars;
GList *tokens;
GList *current_token;
} GPlateTemplatePrivate;
/******************************************************************************
* Globals
*****************************************************************************/
static GObjectClass *parent_class = NULL;
/******************************************************************************
* Collection Stuff
*****************************************************************************/
static GPlateVariable *
gplate_template_find_variable(const GPlateCollection *collection,
const gchar *name)
{
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(collection);
return gplate_collection_find_variable(GPLATE_COLLECTION(priv->vars),
name);
}
static gboolean
gplate_template_add_variable(GPlateCollection *collection,
GPlateVariable *variable)
{
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(collection);
return gplate_collection_add_variable(GPLATE_COLLECTION(priv->vars),
variable);
}
static void
gplate_template_collection_init(GPlateCollectionIface *iface) {
iface->find_variable = gplate_template_find_variable;
iface->add_variable = gplate_template_add_variable;
}
/******************************************************************************
* Template Stuff
*****************************************************************************/
static void
gplate_template_update_pattern_helper(GType tag, const gchar *prefix,
const gchar *suffix, gpointer d)
{
GString *str = (GString *)d;
const gchar *sep = "";
/* don't add fall through tags to the regex */
if(!prefix || !suffix)
return;
if(str->len > 1)
sep = "|";
g_string_append_printf(str, "%s%s.*?%s", sep, prefix, suffix);
}
static gchar *
gplate_template_update_pattern(GPlateTemplate *tplate) {
GString *str = g_string_new("(");
gchar *ret = NULL;
gplate_library_tags_foreach(gplate_template_update_pattern_helper, str);
g_string_append_printf(str, "%s", ")");
ret = str->str;
g_string_free(str, FALSE);
return ret;
}
typedef struct {
const gchar *contents;
GType type;
} GPlateTemplateFindTagData;
static void
gplate_template_find_tag_helper(GType tag, const gchar *prefix,
const gchar *suffix, gpointer d)
{
GPlateTemplateFindTagData *data = (GPlateTemplateFindTagData *)d;
gchar *pattern = NULL;
/* if our tag info doesn't have a prefix or a suffix, we drop out. */
if(!prefix || !suffix)
return;
/* create our pattern string */
pattern = g_strdup_printf("%s.*?%s", prefix, suffix);
/* look for a match:
*
* G_REGEX_DOTALL is REQUIRED so that newlines get counted.
*
* ie: {{\nfoo\n}} would be treated as a fall through if not for it.
*/
if(g_regex_match_simple(pattern, data->contents, G_REGEX_DOTALL, 0))
data->type = tag;
/* cleanup */
g_free(pattern);
}
static GType
gplate_template_find_tag(GPlateTemplate *tplate, const gchar *contents) {
GPlateTemplateFindTagData d;
d.contents = contents;
d.type = G_TYPE_INVALID;
gplate_library_tags_foreach(gplate_template_find_tag_helper, &d);
if(d.type == G_TYPE_INVALID)
d.type = gplate_library_get_default_tag();
return d.type;
}
static GList *
gplate_template_real_tokenize(GPlateTemplate *tplate, const gchar *tplate_string,
GError **error)
{
GList *ret = NULL;
GRegex *regex1 = NULL;
gchar **matches = NULL;
gchar *pattern = NULL;
gint i = 0;
pattern = gplate_template_update_pattern(tplate);
regex1 = g_regex_new(pattern, G_REGEX_OPTIMIZE, 0, error);
if(!regex1) {
g_free(pattern);
return NULL;
}
g_free(pattern);
matches = g_regex_split(regex1, tplate_string, 0);
for(i = 0; matches[i]; i++) {
GPlateTag *tag = NULL;
GType type = G_TYPE_INVALID;
gchar *prefix = NULL, *suffix = NULL;
if(g_utf8_strlen(matches[i], -1) <= 0)
continue;
type = gplate_template_find_tag(tplate, matches[i]);
if(!gplate_library_lookup_tag(type, &prefix, &suffix, error))
continue;
if(!prefix && !suffix) {
/* it's a fall through tag */
tag = g_object_new(type,
"contents", matches[i],
NULL);
} else {
/* it's a normal tag, strip it's prefix and suffix */
GRegex *regex2 = NULL;
GMatchInfo *match = NULL;
gchar *pattern = NULL, *contents = NULL;
pattern = g_strdup_printf("%s(.*?)%s", prefix, suffix);
regex2 = g_regex_new(pattern,
G_REGEX_OPTIMIZE | G_REGEX_DOTALL,
0, error);
g_free(pattern);
if(!regex2) {
GList *l = NULL;
for(l = ret; l; l = l->next)
g_object_unref(G_OBJECT(l->data));
g_list_free(ret);
return NULL;
}
/* get our match info */
if(!g_regex_match(regex2, matches[i], 0, &match)) {
GList *l = NULL;
g_match_info_free(match);
for(l = ret; l; l = l->next)
g_object_unref(G_OBJECT(l->data));
g_list_free(ret);
return NULL;
}
contents = g_match_info_fetch(match, 1);
g_strstrip(contents);
g_match_info_free(match);
tag = g_object_new(type,
"contents", contents,
"template", tplate,
NULL);
g_free(contents);
}
ret = g_list_prepend(ret, tag);
}
g_strfreev(matches);
g_regex_unref(regex1);
ret = g_list_reverse(ret);
return ret;
}
static GPlateTag *
gplate_template_real_first_tag(GPlateTemplate *tplate) {
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(tplate);
GPlateTag *tag = NULL;
priv->current_token = priv->tokens;
if(priv->current_token && GPLATE_IS_TAG(priv->current_token->data))
tag = GPLATE_TAG(priv->current_token->data);
return tag;
}
static GPlateTag *
gplate_template_real_last_tag(GPlateTemplate *tplate) {
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(tplate);
GPlateTag *tag = NULL;
priv->current_token = g_list_last(priv->tokens);
if(priv->current_token && GPLATE_IS_TAG(priv->current_token->data))
tag = GPLATE_TAG(priv->current_token->data);
return tag;
}
static GPlateTag *
gplate_template_real_current_tag(GPlateTemplate *tplate) {
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(tplate);
GPlateTag *tag = NULL;
if(priv->current_token && GPLATE_IS_TAG(priv->current_token->data))
tag = GPLATE_TAG(priv->current_token->data);
return tag;
}
static GPlateTag *
gplate_template_real_next_tag(GPlateTemplate *tplate) {
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(tplate);
GPlateTag *tag = NULL;
priv->current_token = g_list_next(priv->current_token);
if(priv->current_token && GPLATE_IS_TAG(priv->current_token->data))
tag = GPLATE_TAG(priv->current_token->data);
return tag;
}
static GPlateTag *
gplate_template_real_previous_tag(GPlateTemplate *tplate) {
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(tplate);
GPlateTag *tag = NULL;
priv->current_token = g_list_previous(priv->current_token);
if(priv->current_token && GPLATE_IS_TAG(priv->current_token->data))
tag = GPLATE_TAG(priv->current_token->data);
return tag;
}
static GPlateTag *
gplate_template_real_nth_tag(GPlateTemplate *tplate, guint nth) {
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(tplate);
GPlateTag *tag = NULL;
priv->current_token = g_list_nth(priv->current_token, nth);
if(priv->current_token && GPLATE_IS_TAG(priv->current_token->data))
tag = GPLATE_TAG(priv->current_token->data);
return tag;
}
static GPlateTag *
gplate_template_real_nth_previous_tag(GPlateTemplate *tplate, guint nth) {
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(tplate);
GPlateTag *tag = NULL;
priv->current_token = g_list_nth_prev(priv->current_token, nth);
if(priv->current_token && GPLATE_IS_TAG(priv->current_token->data))
tag = GPLATE_TAG(priv->current_token->data);
return tag;
}
static void
gplate_template_real_insert_tags(GPlateTemplate *tplate, GList *tags) {
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(tplate);
/* check if our current node is valid */
if(!priv->current_token) {
/* check if we have a list of tokens */
if(!priv->tokens) {
/* no existing tokens, set it to tags and exit */
priv->tokens = tags;
} else {
/* we have tokens, but we've read the last one, move back to the
* last one.
*/
priv->tokens = g_list_concat(priv->tokens, tags);
}
} else {
GList *tmp = NULL;
/* we have a valid node selected, save it's next node */
tmp = priv->current_token->next;
/* now mangle current and the first node in tags to shove tags in */
priv->current_token->next = tags;
tags->prev = priv->current_token;
/* if the original next node is valid, we need to put them back into
* place
*/
if(tmp) {
GList *last = g_list_last(tags);
last->next = tmp;
tmp->prev = last;
}
}
}
/******************************************************************************
* Object Stuff
*****************************************************************************/
static void
gplate_template_finalize(GObject *obj) {
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(obj);
GList *l = NULL;
g_object_unref(priv->vars);
for(l = priv->tokens; l; l = l->next)
g_object_unref(G_OBJECT(l->data));
g_list_free(priv->tokens);
G_OBJECT_CLASS(parent_class)->finalize(obj);
}
static void
gplate_template_class_init(GPlateTemplateClass *klass) {
GObjectClass *obj_class = G_OBJECT_CLASS(klass);
parent_class = g_type_class_peek_parent(klass);
g_type_class_add_private(klass, sizeof(GPlateTemplatePrivate));
obj_class->finalize = gplate_template_finalize;
klass->tokenize = gplate_template_real_tokenize;
klass->first_tag = gplate_template_real_first_tag;
klass->last_tag = gplate_template_real_last_tag;
klass->current_tag = gplate_template_real_current_tag;
klass->next_tag = gplate_template_real_next_tag;
klass->previous_tag = gplate_template_real_previous_tag;
klass->nth_tag = gplate_template_real_nth_tag;
klass->nth_previous_tag = gplate_template_real_nth_previous_tag;
klass->insert_tags = gplate_template_real_insert_tags;
}
static void
gplate_template_init(GTypeInstance *self, gpointer klass) {
GPlateTemplatePrivate *priv = GPLATE_TEMPLATE_GET_PRIVATE(self);
priv->vars = gplate_dictionary_variable_new("dict");
}
/******************************************************************************
* Render helpers
*****************************************************************************/
static inline GPlateFunction *
gplate_template_render_find_function(const gchar *name, GType tag,
gboolean *deffunc, GError **error)
{
GPlateFunction *ret = NULL;
GType function = G_TYPE_INVALID;
gboolean def = FALSE;
function = gplate_library_lookup_function_for_tag(name, tag, NULL);
if(function == G_TYPE_INVALID) {
function = gplate_library_get_default_function_for_tag(tag, NULL);
def = TRUE;
}
if(function != G_TYPE_INVALID) {
ret = g_object_new(function, NULL);
if(deffunc)
*deffunc = def;
}
return ret;
}
static inline gchar *
gplate_template_render_token(GPlateTemplate *tplate, const gchar *contents,
GType tag, GError **error)
{
GPlateFunction *function = NULL;
gchar *name = NULL, *leftovers = NULL, *ret = NULL;
gboolean deffunc = FALSE;
name = gplate_util_get_first_word(contents, &leftovers);
if(!name)
return g_strdup("");
function =
gplate_template_render_find_function(name, tag, &deffunc, error);
if(function) {
if(deffunc)
ret = gplate_function_evaluate(function, tplate, contents);
else
ret = gplate_function_evaluate(function, tplate, leftovers);
g_object_unref(G_OBJECT(function));
}
g_free(name);
g_free(leftovers);
return (ret) ? ret : g_strdup("");
}
/******************************************************************************
* GPlateTemplate API
*****************************************************************************/
GType
gplate_template_get_gtype(void) {
static GType type = 0;
if(type == 0) {
static const GTypeInfo info = {
sizeof(GPlateTemplateClass),
NULL,
NULL,
(GClassInitFunc)gplate_template_class_init,
NULL,
NULL,
sizeof(GPlateTemplate),
0,
gplate_template_init,
};
static const GInterfaceInfo collection_info = {
(GInterfaceInitFunc)gplate_template_collection_init,
NULL,
NULL,
};
type = g_type_register_static(G_TYPE_OBJECT,
"GPlateTemplate",
&info, 0);
g_type_add_interface_static(type, GPLATE_TYPE_COLLECTION,
&collection_info);
}
return type;
}
/**
* gplate_template_new:
*
* Creates a new #GPlateTemplate instance.
*
* Return Value: The new #GPlateTemplate instance.
*/
GPlateTemplate *
gplate_template_new(void) {
return g_object_new(GPLATE_TYPE_TEMPLATE, NULL);
}
/**
* gplate_template_tokenize:
* @tplate: The #GPlateTemplate.
* @tplate_string: The string to be translated.
* @error: The return address for any errors.
*
* Tokenizes @tplate_string into a #GList of #GPlateTag's to be later passed to
* #gplate_template_render.
*
* Normally this is called by #gplate_template_render directly, but there are a
* few corner cases where you may want to call this directly.
*
* Return Value: A #GList of #GPlateTags.
*/
GList *
gplate_template_tokenize(GPlateTemplate *tplate, const gchar *tplate_string,
GError **error)
{
GPlateTemplateClass *klass = NULL;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
g_return_val_if_fail(tplate_string, NULL);
klass = GPLATE_TEMPLATE_GET_CLASS(tplate);
if(klass && klass->tokenize)
return klass->tokenize(tplate, tplate_string, error);
return NULL;
}
/**
* gplate_template_render:
* @tplate: The #GPlateTemplate.
* @tplate_string: The template string to render.
* @error: Return address for errors.
*
* Renders @tplate_string using @tplate.
*
* Return Value: The rendered output.
*/
gchar *
gplate_template_render(GPlateTemplate *tplate, const gchar *tplate_string,
GError **error)
{
GPlateTemplatePrivate *priv = NULL;
GPlateTag *tag = NULL;
GString *str = NULL;
gchar *ret = NULL;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
g_return_val_if_fail(tplate_string, NULL);
priv = GPLATE_TEMPLATE_GET_PRIVATE(tplate);
priv->tokens = gplate_template_tokenize(tplate, tplate_string, error);
if(!priv->tokens)
return g_strdup("");
/* for whatever reason, using g_string_new_len here doesn't work */
str = g_string_new("");
for(tag = gplate_template_first_tag(tplate);
tag;
tag = gplate_template_next_tag(tplate))
{
GType ttype = G_OBJECT_TYPE(tag);
gchar *contents = NULL, *output = NULL;
contents = gplate_tag_get_contents(tag);
output = gplate_template_render_token(tplate, contents, ttype, error);
g_string_append_printf(str, "%s", output);
g_free(contents);
g_free(output);
}
ret = str->str;
g_string_free(str, FALSE);
return ret;
}
/**
* gplate_template_render_file:
* @tplate: The #GPlateTemplate.
* @filename: The file name to render.
* @error: Return address for errors.
*
* Renders @filename using #GPlateTemplate @tplate.
*
* Return Value: The rendered output.
*/
gchar *
gplate_template_render_file(GPlateTemplate *tplate, const gchar *filename,
GError **error)
{
gchar *contents = NULL, *ret = NULL;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
g_return_val_if_fail(filename, NULL);
if(!gplate_util_read_file(filename, &contents, NULL, error))
return FALSE;
ret = gplate_template_render(tplate, contents, error);
g_free(contents);
return ret;
}
/**
* gplate_template_first_tag:
* @tplate: The #GPlateTemplate.
*
* Moves the tag stack to the first tag and returns it.
*
* Return Value: The first tag or NULL.
*/
GPlateTag *
gplate_template_first_tag(GPlateTemplate *tplate) {
GPlateTemplateClass *klass = NULL;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
klass = GPLATE_TEMPLATE_GET_CLASS(tplate);
if(klass && klass->first_tag)
return klass->first_tag(tplate);
return NULL;
}
/**
* gplate_template_last_tag:
* @tplate: The #GPlateTemplate.
*
* Moves the tag stack to the last tag and returns it.
*
* Return Value: The last tag or NULL.
*/
GPlateTag *
gplate_template_last_tag(GPlateTemplate *tplate) {
GPlateTemplateClass *klass = NULL;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
klass = GPLATE_TEMPLATE_GET_CLASS(tplate);
if(klass && klass->last_tag)
return klass->last_tag(tplate);
return NULL;
}
/**
* gplate_template_current_tag:
* @tplate: The #GPlateTemplate.
*
* Returns the current tag.
*
* Return Value: The current tag or NULL.
*/
GPlateTag *
gplate_template_current_tag(GPlateTemplate *tplate) {
GPlateTemplateClass *klass = NULL;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
klass = GPLATE_TEMPLATE_GET_CLASS(tplate);
if(klass && klass->current_tag)
return klass->current_tag(tplate);
return NULL;
}
/**
* gplate_template_next_tag:
* @tplate: The #GPlateTemplate.
*
* Moves the tag stack forward one tag and returns it.
*
* Return Value: The previous tag or NULL.
*/
GPlateTag *
gplate_template_next_tag(GPlateTemplate *tplate) {
GPlateTemplateClass *klass = NULL;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
klass = GPLATE_TEMPLATE_GET_CLASS(tplate);
if(klass && klass->next_tag)
return klass->next_tag(tplate);
return NULL;
}
/**
* gplate_template_previous_tag:
* @tplate: The #GPlateTemplate.
*
* Moves the tag stack back one tag and returns it.
*
* Return Value: The previous tag or NULL.
*/
GPlateTag *
gplate_template_previous_tag(GPlateTemplate *tplate) {
GPlateTemplateClass *klass = NULL;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
klass = GPLATE_TEMPLATE_GET_CLASS(tplate);
if(klass && klass->previous_tag)
return klass->previous_tag(tplate);
return NULL;
}
/**
* gplate_template_nth_tag:
* @tplate: The #GPlateTemplate.
* @nth: The number of tags to move forwards.
*
* Moves the tag stack forward @nth tags and returns that tag.
*
* Return Value: The @nth tag or NULL.
*/
GPlateTag *
gplate_template_nth_tag(GPlateTemplate *tplate, guint nth) {
GPlateTemplateClass *klass = NULL;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
klass = GPLATE_TEMPLATE_GET_CLASS(tplate);
if(klass && klass->nth_tag)
return klass->nth_tag(tplate, nth);
return NULL;
}
/**
* gplate_template_nth_previous_tag:
* @tplate: The #GPlateTemplate.
* @nth: The number of tags to move backwards.
*
* Moves the tag stack back @nth tags and returns that tag.
*
* Return Value: The @nth previous tag or NULL.
*/
GPlateTag *
gplate_template_nth_previous_tag(GPlateTemplate *tplate, guint nth) {
GPlateTemplateClass *klass = NULL;
g_return_val_if_fail(GPLATE_IS_TEMPLATE(tplate), NULL);
klass = GPLATE_TEMPLATE_GET_CLASS(tplate);
if(klass && klass->nth_previous_tag)
return klass->nth_previous_tag(tplate, nth);
return NULL;
}
/**
* gplate_template_insert_tags:
* @tplate: The #GPlateTemplate.
* @tags: A #GList of #GPlateTag's to insert.
*
* Inserts @tags after the current tag in @tplate's stack.
*/
void
gplate_template_insert_tags(GPlateTemplate *tplate, GList *tags) {
GPlateTemplateClass *klass = NULL;
g_return_if_fail(GPLATE_IS_TEMPLATE(tplate));
g_return_if_fail(tags);
klass = GPLATE_TEMPLATE_GET_CLASS(tplate);
if(klass && klass->insert_tags)
klass->insert_tags(tplate, tags);
}