aboutsummaryrefslogtreecommitdiff
path: root/lib/experiment-reader/experiment-reader.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/experiment-reader/experiment-reader.c')
-rw-r--r--lib/experiment-reader/experiment-reader.c607
1 files changed, 0 insertions, 607 deletions
diff --git a/lib/experiment-reader/experiment-reader.c b/lib/experiment-reader/experiment-reader.c
deleted file mode 100644
index c6bf02e..0000000
--- a/lib/experiment-reader/experiment-reader.c
+++ /dev/null
@@ -1,607 +0,0 @@
-/**
- * @file
- * Auxiliary class to handle "session" XML files (augmented Folker).
- * It is a GObject that must be freed using \e g_object_unref.
- */
-
-/*
- * Copyright (C) 2012-2013 Otto-von-Guericke-Universität Magdeburg
- *
- * 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 3 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, see <http://www.gnu.org/licenses/>.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <string.h>
-#include <assert.h>
-
-#include <glib-object.h>
-#include <glib.h>
-#include <glib/gprintf.h>
-
-#include <libxml/tree.h>
-#include <libxml/parser.h>
-#include <libxml/xpath.h>
-#include <libxml/xpathInternals.h>
-
-#include "cclosure-marshallers.h"
-#include "experiment-reader.h"
-
-static void experiment_reader_class_init(ExperimentReaderClass *klass);
-static void experiment_reader_init(ExperimentReader *klass);
-static void experiment_reader_finalize(GObject *gobject);
-
-static gint64 get_timepoint_by_ref(xmlDoc *doc, xmlChar *ref);
-static xmlNode *get_first_element(xmlNode *children, const gchar *name);
-static xmlNode *get_last_element(xmlNode *children, const gchar *name);
-
-static GClosure *experiment_reader_topic_callback_new(ExperimentReaderTopicCallback,
- gpointer);
-static void experiment_reader_topic_callback_invoke(ExperimentReader *reader,
- GClosure *closure,
- const gchar *topic_id,
- gint64 start_time,
- gint64 end_time);
-static gboolean generic_foreach_topic(ExperimentReader *reader, xmlNodeSet *nodes,
- GClosure *closure);
-
-static gint experiment_reader_contrib_cmp(const ExperimentReaderContrib *a,
- const ExperimentReaderContrib *b);
-static void insert_contribution(gint64 start_time, gchar *text, GList **list);
-static inline void process_contribution(xmlDoc *doc, xmlNode *contrib,
- GList **list);
-
-/** @private */
-#define XML_CHAR(STR) \
- ((const xmlChar *)(STR))
-
-/** @private */
-#define EXPERIMENT_READER_GET_PRIVATE(obj) \
- (G_TYPE_INSTANCE_GET_PRIVATE((obj), EXPERIMENT_TYPE_READER, ExperimentReaderPrivate))
-
-/** @private */
-struct _ExperimentReaderPrivate {
- xmlDoc *doc;
-};
-
-/**
- * @private
- * Will create \e experiment_reader_get_type and set
- * \e experiment_reader_parent_class
- */
-G_DEFINE_TYPE(ExperimentReader, experiment_reader, G_TYPE_OBJECT);
-
-static void
-experiment_reader_class_init(ExperimentReaderClass *klass)
-{
- GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
-
- /* gobject_class->dispose = experiment_reader_dispose; */
- gobject_class->finalize = experiment_reader_finalize;
-
- g_type_class_add_private(klass, sizeof(ExperimentReaderPrivate));
-}
-
-static void
-experiment_reader_init(ExperimentReader *klass)
-{
- klass->priv = EXPERIMENT_READER_GET_PRIVATE(klass);
-
- klass->priv->doc = NULL;
-}
-
-static void
-experiment_reader_finalize(GObject *gobject)
-{
- ExperimentReader *reader = EXPERIMENT_READER(gobject);
-
- if (reader->priv->doc != NULL)
- xmlFreeDoc(reader->priv->doc);
-
- /* Chain up to the parent class */
- G_OBJECT_CLASS(experiment_reader_parent_class)->finalize(gobject);
-}
-
-static gint64
-get_timepoint_by_ref(xmlDoc *doc, xmlChar *ref)
-{
- xmlChar expr[255];
-
- xmlXPathContext *xpathCtx;
- xmlXPathObject *xpathObj;
-
- double value;
-
- xpathCtx = xmlXPathNewContext(doc);
- assert(xpathCtx != NULL);
-
- /** @todo precompile XPath expression */
- xmlStrPrintf(expr, sizeof(expr),
- XML_CHAR("/session/timeline/"
- "timepoint[@timepoint-id = '%s']/"
- "@absolute-time"), ref);
-
- xpathObj = xmlXPathEvalExpression(expr, xpathCtx);
- assert(xpathObj != NULL);
-
- value = xmlXPathCastToNumber(xpathObj);
-
- xmlXPathFreeObject(xpathObj);
- xmlXPathFreeContext(xpathCtx);
-
- return (gint64)(value*1000.);
-}
-
-static xmlNode *
-get_first_element(xmlNode *children, const gchar *name)
-{
- for (xmlNode *cur = children; cur != NULL; cur = cur->next)
- if (cur->type == XML_ELEMENT_NODE &&
- !g_strcmp0((const gchar *)cur->name, name))
- return cur;
-
- return NULL;
-}
-
-static xmlNode *
-get_last_element(xmlNode *children, const gchar *name)
-{
- xmlNode *ret = NULL;
-
- for (xmlNode *cur = children; cur != NULL; cur = cur->next)
- if (cur->type == XML_ELEMENT_NODE &&
- !g_strcmp0((const gchar *)cur->name, name))
- ret = cur;
-
- return ret;
-}
-
-static GClosure *
-experiment_reader_topic_callback_new(ExperimentReaderTopicCallback callback,
- gpointer data)
-{
- GClosure *closure = g_cclosure_new(G_CALLBACK(callback), data, NULL);
-
- g_closure_set_marshal(closure,
- experiment_reader_marshal_VOID__STRING_INT64_INT64);
- g_closure_ref(closure);
- g_closure_sink(closure);
-
- return closure;
-}
-
-static void
-experiment_reader_topic_callback_invoke(ExperimentReader *reader,
- GClosure *closure,
- const gchar *topic_id,
- gint64 start_time, gint64 end_time)
-{
- GValue params[4];
-
- memset(params, 0, sizeof(params));
- g_value_init(params + 0, G_TYPE_OBJECT);
- g_value_set_object(params + 0, reader);
- g_value_init(params + 1, G_TYPE_STRING);
- g_value_set_string(params + 1, topic_id);
- g_value_init(params + 2, G_TYPE_INT64);
- g_value_set_int64(params + 2, start_time);
- g_value_init(params + 3, G_TYPE_INT64);
- g_value_set_int64(params + 3, end_time);
-
- g_closure_invoke(closure, NULL, G_N_ELEMENTS(params), params, NULL);
-
- for (gint i = 0; i < G_N_ELEMENTS(params); i++)
- g_value_unset(params + i);
-}
-
-static gboolean
-generic_foreach_topic(ExperimentReader *reader, xmlNodeSet *nodes,
- GClosure *closure)
-{
- if (nodes == NULL)
- return TRUE;
-
- for (int i = 0; i < nodes->nodeNr; i++) {
- xmlNode *cur = nodes->nodeTab[i];
- assert(cur != NULL && cur->type == XML_ELEMENT_NODE);
-
- xmlNode *first_contrib = get_first_element(cur->children,
- "contribution");
- xmlNode *last_contrib = get_last_element(cur->children,
- "contribution");
-
- xmlChar *topic_id = xmlGetProp(cur, XML_CHAR("id"));
- gint64 start_time = -1;
- gint64 end_time = -1;
-
- if (first_contrib != NULL) {
- xmlChar *contrib_start_ref;
-
- contrib_start_ref = xmlGetProp(first_contrib,
- XML_CHAR("start-reference"));
- start_time = get_timepoint_by_ref(reader->priv->doc,
- contrib_start_ref);
- xmlFree(contrib_start_ref);
- }
- if (last_contrib != NULL) {
- xmlChar *contrib_end_ref;
-
- contrib_end_ref = xmlGetProp(last_contrib,
- XML_CHAR("end-reference"));
- end_time = get_timepoint_by_ref(reader->priv->doc,
- contrib_end_ref);
- xmlFree(contrib_end_ref);
- }
-
- experiment_reader_topic_callback_invoke(reader, closure,
- (const gchar *)topic_id,
- start_time, end_time);
-
- xmlFree(topic_id);
- }
-
- return FALSE;
-}
-
-static gint
-experiment_reader_contrib_cmp(const ExperimentReaderContrib *a,
- const ExperimentReaderContrib *b)
-{
- if (a->start_time < b->start_time)
- return -1;
- if (a->start_time > b->start_time)
- return 1;
- return 0;
-}
-
-static void
-insert_contribution(gint64 start_time, gchar *text, GList **list)
-{
- ExperimentReaderContrib *contrib;
-
- if (text == NULL)
- return;
-
- contrib = g_malloc(sizeof(ExperimentReaderContrib) + strlen(text) + 1);
- contrib->start_time = start_time;
- g_stpcpy(contrib->text, g_strchomp(text));
-
- *list = g_list_insert_sorted(*list, contrib,
- (GCompareFunc)experiment_reader_contrib_cmp);
-}
-
-static inline void
-process_contribution(xmlDoc *doc, xmlNode *contrib, GList **list)
-{
- xmlChar *ref;
- gint64 start_time;
-
- gchar *text = NULL;
-
- ref = xmlGetProp(contrib, XML_CHAR("start-reference"));
- start_time = get_timepoint_by_ref(doc, ref);
- xmlFree(ref);
-
- for (xmlNode *cur = contrib->children; cur != NULL; cur = cur->next) {
- xmlChar *content;
- gchar *new;
-
- switch (cur->type) {
- case XML_TEXT_NODE:
- content = xmlNodeGetContent(cur);
-
- new = g_strconcat(text != NULL ? text : "",
- g_strstrip((gchar *)content),
- " ", NULL);
- g_free(text);
- text = new;
-
- xmlFree(content);
- break;
-
- case XML_ELEMENT_NODE:
- if (!xmlStrcmp(cur->name, XML_CHAR("pause"))) {
- xmlChar *duration;
-
- duration = xmlGetProp(cur, XML_CHAR("duration"));
- if (duration == NULL)
- break;
-
- if (!xmlStrcmp(duration, XML_CHAR("micro")) ||
- !xmlStrcmp(duration, XML_CHAR("short")))
- new = g_strconcat(text != NULL ? text : "",
- "... ", NULL);
- else if (text == NULL)
- new = g_strdup("...\n");
- else
- new = g_strconcat(g_strchomp(text),
- "\n", NULL);
- g_free(text);
- text = new;
-
- xmlFree(duration);
- } else if (!xmlStrcmp(cur->name, XML_CHAR("time"))) {
- insert_contribution(start_time, text, list);
- g_free(text);
- text = NULL;
-
- ref = xmlGetProp(cur,
- XML_CHAR("timepoint-reference"));
- start_time = get_timepoint_by_ref(doc, ref);
- xmlFree(ref);
- }
- break;
-
- default:
- break;
- }
- }
-
- insert_contribution(start_time, text, list);
- g_free(text);
-}
-
-/*
- * API
- */
-
-/**
- * @brief Constructs a new ExperimentReader object
- *
- * @param filename Filename of XML file to open
- * @return A new \e ExperimentReader object. Free with \e g_object_unref.
- */
-ExperimentReader *
-experiment_reader_new(const gchar *filename)
-{
- ExperimentReader *reader;
-
- reader = EXPERIMENT_READER(g_object_new(EXPERIMENT_TYPE_READER, NULL));
- reader->priv->doc = xmlParseFile(filename);
- if (reader->priv->doc == NULL) {
- g_object_unref(G_OBJECT(reader));
- return NULL;
- }
-
- /** @todo validate against session.dtd */
-
- return reader;
-}
-
-/**
- * @brief Retrieve list of contributions by speaker
- *
- * Returns a newly-allocated doubly-linked list of
- * \ref ExperimentReaderContrib structures representing all contributions
- * by a given speaker. Every text fragment with a \e timepoint reference is
- * considered a contribution.
- * The list is sorted by the contributions' start times, in ascending order.
- *
- * @sa ExperimentReaderContrib
- * @sa experiment_reader_get_contribution_by_time
- * @sa experiment_reader_free_contributions
- *
- * @param reader \e ExperimentReader instance
- * @param speaker Full name of the speaker (e.g. "Wizard")
- * @return Newly allocated list of contributions (must be freed with
- * \ref experiment_reader_free_contributions)
- */
-GList *
-experiment_reader_get_contributions_by_speaker(ExperimentReader *reader,
- const gchar *speaker)
-{
- GList *list = NULL;
-
- xmlXPathContext *xpathCtx;
- xmlXPathObject *xpathObj;
-
- xmlChar expr[255];
-
- xpathCtx = xmlXPathNewContext(reader->priv->doc);
-
- /* Evaluate xpath expression */
- xmlStrPrintf(expr, sizeof(expr),
- XML_CHAR("//contribution[@speaker-reference = "
- "/session/speakers/speaker[name = '%s']/@speaker-id]"),
- speaker);
- xpathObj = xmlXPathEvalExpression(expr, xpathCtx);
-
- for (int i = 0; i < xpathObj->nodesetval->nodeNr; i++) {
- xmlNode *contrib = xpathObj->nodesetval->nodeTab[i];
-
- process_contribution(reader->priv->doc, contrib, &list);
- }
-
- xmlXPathFreeObject(xpathObj);
- xmlXPathFreeContext(xpathCtx);
-
- return list;
-}
-
-/**
- * @brief Get a contribution by time
- *
- * Gets the closest contribution after the specified time or the last one
- * if there is no contribution after the specified time.
- * The contribution is returned as a pointer into the contribution list
- * so that the list may be traversed by the caller.
- *
- * @param contribs List of \ref ExperimentReaderContrib structures as returned
- * by \ref experiment_reader_get_contributions_by_speaker
- * @param timept Time in milliseconds
- * @return List of contributions beginning with the desired contribution.
- * It is a pointer into contribs and must not be freed directly.
- */
-GList *
-experiment_reader_get_contribution_by_time(GList *contribs, gint64 timept)
-{
- for (GList *cur = contribs; cur != NULL; cur = cur->next) {
- ExperimentReaderContrib *contrib =
- (ExperimentReaderContrib *)cur->data;
-
- if (contrib->start_time > timept ||
- cur->next == NULL)
- return cur;
- }
-
- return NULL;
-}
-
-/**
- * @brief Free list of contributions and associated data
- *
- * @sa experiment_reader_get_contributions_by_speaker
- *
- * @param contribs List of \ref ExperimentReaderContrib structures to free
- */
-void
-experiment_reader_free_contributions(GList *contribs)
-{
- for (GList *cur = contribs; cur != NULL; cur = cur->next)
- g_free(cur->data);
-
- g_list_free(contribs);
-}
-
-/**
- * Calls \e callback with \e userdata for each \b topic in the \b greeting
- * section of the experiment.
- *
- * @param reader \e ExperimentReader instance
- * @param callback Function to invoke
- * @param userdata User data to pass to \e callback
- */
-void
-experiment_reader_foreach_greeting_topic(ExperimentReader *reader,
- ExperimentReaderTopicCallback callback,
- gpointer userdata)
-{
- xmlXPathContext *xpathCtx;
- xmlXPathObject *xpathObj;
- GClosure *closure;
-
- xpathCtx = xmlXPathNewContext(reader->priv->doc);
- xpathObj = xmlXPathEvalExpression(XML_CHAR("/session/greeting/topic"),
- xpathCtx);
-
- closure = experiment_reader_topic_callback_new(callback, userdata);
- generic_foreach_topic(reader, xpathObj->nodesetval, closure);
- g_closure_unref(closure);
-
- xmlXPathFreeObject(xpathObj);
- xmlXPathFreeContext(xpathCtx);
-}
-
-/**
- * Calls \e callback with \e userdata for each \b topic in the
- * \b initial-narrative subsection of the \b experiment section of
- * the experiment.
- *
- * @param reader \e ExperimentReader instance
- * @param callback Function to invoke
- * @param userdata User data to pass to \e callback
- */
-void
-experiment_reader_foreach_exp_initial_narrative_topic(reader, callback, userdata)
- ExperimentReader *reader;
- ExperimentReaderTopicCallback callback;
- gpointer userdata;
-{
- xmlXPathContext *xpathCtx;
- xmlXPathObject *xpathObj;
- GClosure *closure;
-
- xpathCtx = xmlXPathNewContext(reader->priv->doc);
- xpathObj = xmlXPathEvalExpression(XML_CHAR("/session/experiment/"
- "initial-narrative/topic"),
- xpathCtx);
-
- closure = experiment_reader_topic_callback_new(callback, userdata);
- generic_foreach_topic(reader, xpathObj->nodesetval, closure);
- g_closure_unref(closure);
-
- xmlXPathFreeObject(xpathObj);
- xmlXPathFreeContext(xpathCtx);
-}
-
-/**
- * Calls \e callback with \e userdata for each \b topic in a \b phase of
- * the \b last-minute subsection of the \b experiment section of
- * the experiment.
- *
- * @param reader \e ExperimentReader instance
- * @param phase \b Phase section (integer from 1 to 6)
- * @param callback Function to invoke
- * @param userdata User data to pass to \e callback
- */
-void
-experiment_reader_foreach_exp_last_minute_phase_topic(reader, phase, callback, userdata)
- ExperimentReader *reader;
- gint phase;
- ExperimentReaderTopicCallback callback;
- gpointer userdata;
-{
- xmlXPathContext *xpathCtx;
- xmlXPathObject *xpathObj;
- GClosure *closure;
-
- xmlChar expr[255];
-
- xpathCtx = xmlXPathNewContext(reader->priv->doc);
-
- /* Evaluate xpath expression */
- xmlStrPrintf(expr, sizeof(expr),
- XML_CHAR("/session/experiment/last-minute/"
- "phase[@id = '%d']/topic"),
- phase);
- xpathObj = xmlXPathEvalExpression(expr, xpathCtx);
-
- closure = experiment_reader_topic_callback_new(callback, userdata);
- generic_foreach_topic(reader, xpathObj->nodesetval, closure);
- g_closure_unref(closure);
-
- xmlXPathFreeObject(xpathObj);
- xmlXPathFreeContext(xpathCtx);
-}
-
-/**
- * Calls \e callback with \e userdata for each \b topic in the \b farewell
- * section of the experiment.
- *
- * @param reader \e ExperimentReader instance
- * @param callback Function to invoke
- * @param userdata User data to pass to \e callback
- */
-void
-experiment_reader_foreach_farewell_topic(ExperimentReader *reader,
- ExperimentReaderTopicCallback callback,
- gpointer userdata)
-{
- xmlXPathContext *xpathCtx;
- xmlXPathObject *xpathObj;
- GClosure *closure;
-
- xpathCtx = xmlXPathNewContext(reader->priv->doc);
- xpathObj = xmlXPathEvalExpression(XML_CHAR("/session/farewell/topic"),
- xpathCtx);
-
- closure = experiment_reader_topic_callback_new(callback, userdata);
- generic_foreach_topic(reader, xpathObj->nodesetval, closure);
- g_closure_unref(closure);
-
- xmlXPathFreeObject(xpathObj);
- xmlXPathFreeContext(xpathCtx);
-}