diff --git a/.gitignore b/.gitignore
index 5dd5799..9e7925a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -71,7 +71,6 @@ Makefile.in
/src/matrix-event-call-hangup.c
/src/matrix-event-call-base.c
/src/matrix-glib-0.0.pc
-/src/matrix-message-base.c
/src/matrix-message-text.c
/src/matrix-message-emote.c
/src/matrix-message-notice.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 1ecda61..07c3503 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -38,7 +38,6 @@ libmatrix_glib_0_0_la_VALA_SOURCES = \
matrix-event-call-candidates.vala \
matrix-event-call-answer.vala \
matrix-event-call-hangup.vala \
- matrix-message-base.vala \
matrix-message-text.vala \
matrix-message-emote.vala \
matrix-message-notice.vala \
@@ -104,6 +103,7 @@ INST_H_SRC_FILES = \
matrix-types.h \
matrix-compacts.h \
matrix-event-base.h \
+ matrix-message-base.h \
matrix-event-room-base.h \
matrix-event-state-base.h \
matrix-event-tag.h \
@@ -136,6 +136,7 @@ libmatrix_glib_0_0_la_SOURCES = \
matrix-types.c \
matrix-compacts.c \
matrix-event-base.c \
+ matrix-message-base.c \
matrix-event-tag.c \
matrix-event-presence.c \
matrix-event-room-member.c \
diff --git a/src/matrix-message-base.c b/src/matrix-message-base.c
new file mode 100644
index 0000000..f11691f
--- /dev/null
+++ b/src/matrix-message-base.c
@@ -0,0 +1,683 @@
+/*
+ * This file is part of matrix-glib-sdk
+ *
+ * matrix-glib-sdk is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * matrix-glib-sdk 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with matrix-glib-sdk. If not, see
+ * .
+ */
+
+#include "matrix-message-base.h"
+#include "matrix-types.h"
+#include "matrix-enumtypes.h"
+#include "config.h"
+
+/**
+ * SECTION:message-handling
+ * @short_description: Message handling
+ * @title: Handling message objects
+ * @section_id: message_handling
+ *
+ * To handle message objects, one can use the built-in message classes like #MatrixMessageText,
+ * or, for non-standard message types, implement their own by subclassing #MatrixMessageBase.
+ *
+ * After the class is registered in the GType system, one should call
+ * matrix_message_register_type():
+ *
+ * |[
+ * G_DEFINE_TYPE(MyCustomMessage, my_custom_message, MATRIX_MESSAGE_TYPE_BASE);
+ *
+ * // implement the from_json() and to_json() methods
+ *
+ * void
+ * some_func(JsonNode *custom_message_data)
+ * {
+ * GError *error = NULL;
+ * MatrixMessageBase *message;
+ *
+ * matrix_message_register_type("my.custom.message", my_custom_message_get_type(), NULL);
+ *
+ * message = matrix_message_base_new_from_json(json_data, &error);
+ *
+ * if (error != NULL) {
+ * g_error("Could not parse JSON data as a message.");
+ *
+ * return;
+ * }
+ *
+ * if (!MY_IS_CUSTOM_MESSAGE(message)) {
+ * g_error("The message is not of 'my.custom.message'!");
+ *
+ * return;
+ * }
+ *
+ * // Do something with message
+ * }
+ * ]|
+ *
+ * Registered message types are also used by classes implementing the #MatrixClient interface.
+ */
+
+/**
+ * SECTION:matrix-message-base
+ * @short_description: Base class for Matrix messages
+ * @title: Message handler base class
+ *
+ * Base class for Matrix message handlers.
+ */
+
+enum {
+ PROP_0,
+ PROP_MESSAGE_TYPE,
+ PROP_BODY,
+ PROP_JSON,
+ NUM_PROPS
+};
+
+static GParamSpec *matrix_message_base_properties[NUM_PROPS];
+
+typedef struct {
+ gchar* _message_type;
+ gchar* _body;
+ gboolean _inited;
+ GError* _construct_error;
+ JsonNode* _json;
+} MatrixMessageBasePrivate;
+
+
+GHashTable *matrix_message_type_handlers = NULL;
+
+static void matrix_message_base_g_initable_interface_init(GInitableIface *iface);
+
+/**
+ * MatrixMessageBase:
+ *
+ * Base class for message handlers.
+ */
+
+/**
+ * MatrixMessageBaseClass:
+ * @from_json: function to initialise a #MatrixMessageBase derived object from JSON data. Must be
+ * implemented.
+ * @to_json: function to serialise a #MatrixMessageBase derived object to JSON. Must be
+ * implemented.
+ *
+ * The class structure for the #MatrixMessageBase type.
+ */
+G_DEFINE_TYPE_EXTENDED(MatrixMessageBase, matrix_message_base, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT, G_ADD_PRIVATE(MatrixMessageBase) G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, matrix_message_base_g_initable_interface_init));
+
+static gboolean
+matrix_message_base_real_init(GInitable *g_initable, GCancellable *cancellable, GError **error)
+{
+ MatrixMessageBasePrivate *priv = matrix_message_base_get_instance_private(MATRIX_MESSAGE_BASE(g_initable));
+
+ if (cancellable != NULL) {
+ g_set_error(error, MATRIX_ERROR, MATRIX_ERROR_UNSUPPORTED,
+ "Cancellable initialization not supported");
+
+ return FALSE;
+ }
+
+ if (priv->_construct_error != NULL) {
+ g_propagate_error(error, priv->_construct_error);
+
+ return FALSE;
+ }
+
+ priv->_inited = TRUE;
+
+ return TRUE;
+}
+
+static void
+matrix_message_base_initialize_from_json(MatrixMessageBase *matrix_message_base, JsonNode *json_data, GError **error)
+{
+ JsonGenerator *gen;
+ GError *inner_error = NULL;
+
+ if (json_node_get_node_type(json_data) != JSON_TYPE_OBJECT) {
+ g_set_error(error, MATRIX_ERROR, MATRIX_ERROR_INVALID_FORMAT,
+ "The message is not valid");
+
+ return;
+ }
+
+ gen = json_generator_new();
+ json_generator_set_root(gen, json_data);
+
+ matrix_message_base_from_json(matrix_message_base, json_data, &inner_error);
+
+ g_object_unref(gen);
+
+ if (inner_error != NULL) {
+ g_propagate_error(error, inner_error);
+ }
+}
+
+/**
+ * matrix_message_base_new_from_json: (constructor)
+ * @json_data: a #JsonNode containing a Matrix message
+ * @error: a #GError, or NULL to ignore errors
+ *
+ * Create a new message object from the message contained in @json_data.
+ *
+ * The exact type of the returned object is based on the message contents (precisely, its
+ * `msgtype` field). This function uses matrix_message_get_handler() internally to find the
+ * message handler for the message type; if there is none, this function returns NULL, but doesn’t
+ * set @error. If there is an error during reading or processing the JSON data, NULL is returned,
+ * and @error is set accordingly.
+ *
+ * See the description of matrix_message_register_type() for more details.
+ *
+ * Returns: (transfer full): a new #MatrixMessageBase object or NULL in case of an error
+ */
+MatrixMessageBase *
+matrix_message_base_new_from_json(JsonNode *json_data, GError **error)
+{
+ JsonObject *root;
+ JsonNode *node;
+ GType msg_gtype;
+ MatrixMessageBase *ret;
+ GError *inner_error = NULL;
+
+ g_return_val_if_fail(json_data != NULL, NULL);
+
+ root = json_node_get_object(json_data);
+
+ if ((node = json_object_get_member(root, "msgtype")) == NULL) {
+ g_set_error(error, MATRIX_ERROR, MATRIX_ERROR_INCOMPLETE,
+ "Can not process a message without msgtype");
+
+ return NULL;
+ }
+
+ if ((msg_gtype = matrix_message_get_handler(json_node_get_string(node))) == G_TYPE_NONE) {
+ return NULL;
+ }
+
+ ret = (MatrixMessageBase *)g_object_new(msg_gtype,
+ "json", json_data,
+ NULL);
+
+ g_initable_init(G_INITABLE(ret), NULL, &inner_error);
+
+ if (inner_error != NULL) {
+ g_propagate_error(error, inner_error);
+ g_object_unref(ret);
+
+ return NULL;
+ }
+
+ return ret;
+}
+
+static void
+matrix_message_base_real_from_json(MatrixMessageBase *matrix_message_base, JsonNode *json_data, GError **error)
+{
+ MatrixMessageBasePrivate *priv;
+ JsonObject *root;
+ JsonNode *node;
+
+ g_return_if_fail (json_data != NULL);
+
+ priv = matrix_message_base_get_instance_private(matrix_message_base);
+ root = json_node_get_object(json_data);
+
+ if ((node = json_object_get_member(root, "msgtype")) != NULL) {
+ g_free(priv->_message_type);
+ priv->_message_type = g_strdup(json_node_get_string(node));
+ } else if (DEBUG) {
+ g_warning("msgtype is not present in a message");
+ }
+
+ if ((node = json_object_get_member(root, "body")) != NULL) {
+ g_free(priv->_body);
+ priv->_body = g_strdup(json_node_get_string(node));
+ } else if (DEBUG) {
+ g_warning("body is not presente in a message");
+ }
+}
+
+/**
+ * matrix_message_base_from_json: (virtual from_json)
+ * @message: An object derived from the #MatrixMessageBase class
+ * @json_data: the #JsonNode to initialise to set object properties from
+ * @error: a #GError, or NULL to ignore errors
+ *
+ * Initializes the message object using the data in @json_data.
+ *
+ * For #MatrixMessageBase objects, this method reads the `msgtype` and `body` fields, saving them
+ * to the corresponding properties. Subclasses should implement this method to initialize
+ * themselves from JSON data.
+ */
+void
+matrix_message_base_from_json(MatrixMessageBase *matrix_message_base, JsonNode *json_data, GError **error)
+{
+ g_return_if_fail(matrix_message_base != NULL);
+
+ MATRIX_MESSAGE_BASE_GET_CLASS(matrix_message_base)->from_json(matrix_message_base, json_data, error);
+}
+
+static void
+matrix_message_base_real_to_json(MatrixMessageBase *matrix_message_base, JsonNode *json_data, GError **error)
+{
+ MatrixMessageBasePrivate *priv;
+ JsonObject *root;
+
+ g_return_if_fail(json_data != NULL);
+
+ priv = matrix_message_base_get_instance_private(matrix_message_base);
+ root = json_node_get_object(json_data);
+
+ if (priv->_message_type == NULL) {
+ g_set_error(error, MATRIX_ERROR, MATRIX_ERROR_INCOMPLETE,
+ "Won't generate a message with an empty msgtype");
+
+ return;
+ }
+
+ if (priv->_body == NULL) {
+ g_set_error(error, MATRIX_ERROR, MATRIX_ERROR_INCOMPLETE,
+ "Won't generate a message without body");
+
+ return;
+ }
+
+ json_object_set_string_member(root, "msgtype", priv->_message_type);
+ json_object_set_string_member(root, "body", priv->_body);
+}
+
+/**
+ * matrix_message_base_to_json: (virtual to_json)
+ * @message: An object derived from the #MatrixMessageBase class
+ * @json_data: A #JsonNode that must be initialised as a #JSON_NODE_OBJECT
+ * @error: A #GError, or NULL to ignore errors
+ *
+ * Serializes the message object into a #JsonNode in @json_data. Subclasses should implement this
+ * method to export their data to JSON.
+ */
+void
+matrix_message_base_to_json(MatrixMessageBase *matrix_message_base, JsonNode *json_data, GError **error)
+{
+ g_return_if_fail(matrix_message_base != NULL);
+
+ MATRIX_MESSAGE_BASE_GET_CLASS(matrix_message_base)->to_json(matrix_message_base, json_data, error);
+}
+
+/**
+ * matrix_message_base_construct:
+ * @object_type: the #GType of the object to be created
+ *
+ * Returns: (transfer full): a new instance of @object_type
+ */
+MatrixMessageBase *
+matrix_message_base_construct(GType object_type)
+{
+ return (MatrixMessageBase *)g_object_new(object_type, NULL);
+}
+
+/**
+ * matrix_message_base_get_message_type:
+ * @message: an object derived from the #MatrixMessageBase class
+ *
+ * Get the message type of @message.
+ *
+ * Returns: (transfer none): the message type
+ */
+const gchar *
+matrix_message_base_get_message_type(MatrixMessageBase *matrix_message_base)
+{
+ MatrixMessageBasePrivate *priv;
+
+ g_return_val_if_fail(matrix_message_base != NULL, NULL);
+
+ priv = matrix_message_base_get_instance_private(matrix_message_base);
+
+ return priv->_message_type;
+}
+
+/**
+ * matrix_message_base_set_message_type:
+ * @message: an object derived from the #MatrixMessageBase class
+ * @message_type: (transfer none): the message type to set
+ *
+ * Set the message type of @message.
+ */
+void
+matrix_message_base_set_message_type(MatrixMessageBase *matrix_message_base, const gchar *message_type)
+{
+ MatrixMessageBasePrivate *priv;
+
+ g_return_if_fail(matrix_message_base != NULL);
+
+ priv = matrix_message_base_get_instance_private(matrix_message_base);
+
+ if (g_strcmp0(message_type, priv->_message_type) != 0) {
+ g_free(priv->_message_type);
+ priv->_message_type = g_strdup(message_type);
+
+ g_object_notify_by_pspec((GObject *)matrix_message_base, matrix_message_base_properties[PROP_MESSAGE_TYPE]);
+ }
+}
+
+/**
+ * matrix_message_base_get_body:
+ * @message: an object derived from the #MatrixMessageBase class
+ *
+ * Get the body of the message. For some message type this can be used as a fallback content.
+ *
+ * Returns: (transfer none): the body of @message
+ */
+const gchar *
+matrix_message_base_get_body(MatrixMessageBase *matrix_message_base)
+{
+ MatrixMessageBasePrivate *priv;
+
+ g_return_val_if_fail(matrix_message_base != NULL, NULL);
+
+ priv = matrix_message_base_get_instance_private(matrix_message_base);
+
+ return priv->_body;
+}
+
+/**
+ * matrix_message_base_set_body:
+ * @message: an object derived from the #MatrixMessageBase class
+ * @body: (transfer none): the body to set
+ *
+ * Set the body of the message.
+ */
+void
+matrix_message_base_set_body(MatrixMessageBase *matrix_message_base, const gchar *body)
+{
+ MatrixMessageBasePrivate *priv;
+
+ g_return_if_fail(matrix_message_base != NULL);
+
+ priv = matrix_message_base_get_instance_private(matrix_message_base);
+
+ if (g_strcmp0(body, priv->_body) != 0) {
+ g_free(priv->_body);
+ priv->_body = g_strdup(body);
+
+ g_object_notify_by_pspec((GObject *)matrix_message_base, matrix_message_base_properties[PROP_BODY]);
+ }
+}
+
+/**
+ * matrix_message_base_get_json:
+ * @message: an object derived from the #MatrixMessageBase class
+ * @error: a #GError, or NULL to ignore errors
+ *
+ * Get the #MatrixMessageBase:json property of @message. If an error happens during
+ * serialization, this function returns NULL, and sets @error accordingly.
+ *
+ * This function calls matrix_message_base_to_json() internally.
+ */
+JsonNode *
+matrix_message_base_get_json(MatrixMessageBase *matrix_message_base, GError **error)
+{
+ MatrixMessageBasePrivate *priv;
+ GError *inner_error = NULL;
+
+ g_return_val_if_fail(matrix_message_base != NULL, NULL);
+
+ priv = matrix_message_base_get_instance_private(matrix_message_base);
+ priv->_json = json_node_new(JSON_TYPE_OBJECT);
+ json_node_set_object(priv->_json, json_object_new());
+
+ matrix_message_base_to_json(matrix_message_base, priv->_json, &inner_error);
+
+ if (inner_error != NULL) {
+ g_propagate_error(error, inner_error);
+
+ json_node_unref(priv->_json);
+ priv->_json = NULL;
+
+ return NULL;
+ }
+
+ return priv->_json;
+}
+
+static void
+matrix_message_base_set_json(MatrixMessageBase *matrix_message_base, JsonNode *json_data)
+{
+ GError *inner_error = NULL;
+
+ g_return_if_fail(matrix_message_base != NULL);
+
+ matrix_message_base_initialize_from_json(matrix_message_base, json_data, &inner_error);
+
+ if (inner_error != NULL) {
+ g_error("Unable to initialise MatrixMessageBase from JSON: %s", inner_error->message);
+
+ g_clear_error(&inner_error);
+
+ return;
+ }
+
+ g_object_notify_by_pspec((GObject *)matrix_message_base, matrix_message_base_properties[PROP_JSON]);
+}
+
+static void
+matrix_message_base_finalize(GObject *gobject)
+{
+ MatrixMessageBasePrivate *priv = matrix_message_base_get_instance_private(MATRIX_MESSAGE_BASE(gobject));
+
+ g_free(priv->_message_type);
+ g_free(priv->_body);
+ g_clear_error(&priv->_construct_error);
+ json_node_unref(priv->_json);
+
+ G_OBJECT_CLASS (matrix_message_base_parent_class)->finalize(gobject);
+}
+
+static void
+matrix_message_base_get_property(GObject *gobject, guint property_id, GValue *value, GParamSpec *pspec)
+{
+ MatrixMessageBase *matrix_message_base = MATRIX_MESSAGE_BASE(gobject);
+
+ switch (property_id) {
+ case PROP_MESSAGE_TYPE:
+ g_value_set_string(value, matrix_message_base_get_message_type(matrix_message_base));
+
+ break;
+ case PROP_BODY:
+ g_value_set_string(value, matrix_message_base_get_body(matrix_message_base));
+
+ break;
+ case PROP_JSON:
+ g_value_set_boxed(value, matrix_message_base_get_json(matrix_message_base, NULL));
+
+ break;
+ default:
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id, pspec);
+
+ break;
+ }
+}
+
+static void
+matrix_message_base_set_property(GObject *gobject, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+ MatrixMessageBase *matrix_message_base = MATRIX_MESSAGE_BASE(gobject);
+
+ switch (property_id) {
+ case PROP_MESSAGE_TYPE:
+ matrix_message_base_set_message_type(matrix_message_base, g_value_get_string(value));
+
+ break;
+ case PROP_BODY:
+ matrix_message_base_set_body(matrix_message_base, g_value_get_string(value));
+
+ break;
+ case PROP_JSON:
+ matrix_message_base_set_json(matrix_message_base, g_value_get_boxed(value));
+
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id, pspec);
+
+ break;
+ }
+}
+
+static void
+matrix_message_base_class_init(MatrixMessageBaseClass *klass)
+{
+ klass->from_json = matrix_message_base_real_from_json;
+ klass->to_json = matrix_message_base_real_to_json;
+ G_OBJECT_CLASS(klass)->get_property = matrix_message_base_get_property;
+ G_OBJECT_CLASS(klass)->set_property = matrix_message_base_set_property;
+ G_OBJECT_CLASS(klass)->finalize = matrix_message_base_finalize;
+
+ /**
+ * MatrixMessageBase:message-type:
+ *
+ * The message type.
+ */
+ matrix_message_base_properties[PROP_MESSAGE_TYPE] = g_param_spec_string(
+ "message-type", "message-type", "message-type",
+ NULL,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
+ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_MESSAGE_TYPE, matrix_message_base_properties[PROP_MESSAGE_TYPE]);
+
+ /**
+ * MatrixMessageBase:body:
+ *
+ * The body of the message.
+ */
+ matrix_message_base_properties[PROP_BODY] = g_param_spec_string(
+ "body", "body", "body",
+ NULL,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
+ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_BODY, matrix_message_base_properties[PROP_BODY]);
+
+ /**
+ * MatrixMessageBase:json:
+ *
+ * The JSON node corresponding to the message.
+ */
+ matrix_message_base_properties[PROP_JSON] = g_param_spec_boxed(
+ "json", "json", "json",
+ JSON_TYPE_NODE,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_JSON, matrix_message_base_properties[PROP_JSON]);
+}
+
+static void
+matrix_message_base_g_initable_interface_init(GInitableIface *iface)
+{
+
+ iface->init = matrix_message_base_real_init;
+}
+
+static void
+matrix_message_base_init(MatrixMessageBase *matrix_message_base) {
+ MatrixMessageBasePrivate *priv = matrix_message_base_get_instance_private(matrix_message_base);
+
+ priv->_message_type = NULL;
+ priv->_body = NULL;
+ priv->_inited = FALSE;
+ priv->_construct_error = NULL;
+ priv->_json = NULL;
+}
+
+/**
+ * matrix_message_get_handler:
+ * @message_type: (transfer none): the message type to look up
+ *
+ * Get the #GType of the class that is registered to handle messages with type @message_type.
+ *
+ * Returns: a #GType, or #G_TYPE_NONE if no handler is registered
+ */
+GType
+matrix_message_get_handler(const gchar *message_type)
+{
+ GTypeClass* klass = NULL;
+
+ g_return_val_if_fail(message_type != NULL, G_TYPE_NONE);
+
+ if (matrix_message_type_handlers != NULL) {
+ if ((klass = (GTypeClass *)g_hash_table_lookup (matrix_message_type_handlers, message_type)) != NULL) {
+ return G_TYPE_FROM_CLASS(klass);
+ }
+ }
+
+ if (DEBUG) {
+ g_warning ("matrix-message-base.vala:192: No registered type for message %s", message_type);
+ }
+
+ return G_TYPE_NONE;
+}
+
+/**
+ * matrix_message_register_type:
+ * @message_type: (transfer none): the type of the message
+ * @message_gtype: the #GType of the message’s handler
+ * @error: a #GError, or NULL to ignore errors
+ *
+ * Register @message_type to be handled by the type @message_gtype. If the message type is
+ * already registered, @error is set to #MATRIX_ERROR_ALREADY_EXISTS; in such cases, one should
+ * call matrix_message_unregister_type() first.
+ *
+ * If @message_gtype is not a subclass of #MatrixMessageBase, @error is sot to
+ * #MATRIX_ERROR_INVALID_TYPE.
+ */
+void
+matrix_message_register_type(const gchar *message_type, GType message_gtype, GError **error)
+{
+ gchar *key;
+ GTypeClass *value;
+
+ g_return_if_fail(message_type != NULL);
+
+ if (!g_type_is_a (message_gtype, MATRIX_MESSAGE_TYPE_BASE)) {
+ g_set_error(error, MATRIX_ERROR, MATRIX_ERROR_INVALID_TYPE, "Invalid message handler type");
+
+ return;
+ }
+
+ // Report an error if the message type is already registered
+ if ((matrix_message_type_handlers != NULL) && g_hash_table_lookup_extended(matrix_message_type_handlers, message_type, NULL, NULL)) {
+ g_set_error(error, MATRIX_ERROR, MATRIX_ERROR_ALREADY_EXISTS, "This message type is already registered.");
+
+ return;
+ }
+
+ if (matrix_message_type_handlers == NULL) {
+ matrix_message_type_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_type_class_unref);
+ }
+
+ key = g_strdup (message_type);
+ value = g_type_class_ref(message_gtype);
+ g_hash_table_insert(matrix_message_type_handlers, key, value);
+}
+
+/**
+ * matrix_message_unregister_type:
+ * @message_type: (transfer none): the message type to remove
+ *
+ * Unregister @message_type’s message handler. It is safe to call this on a message type that is
+ * not registered yet, as such calls will be safely ignored.
+ */
+void
+matrix_message_unregister_type(const gchar *message_type)
+{
+ g_return_if_fail(message_type != NULL);
+
+ if (matrix_message_type_handlers != NULL) {
+ g_hash_table_remove (matrix_message_type_handlers, message_type);
+ }
+}
diff --git a/src/matrix-message-base.h b/src/matrix-message-base.h
new file mode 100644
index 0000000..462d67f
--- /dev/null
+++ b/src/matrix-message-base.h
@@ -0,0 +1,66 @@
+/*
+ * This file is part of matrix-glib-sdk
+ *
+ * matrix-glib-sdk is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * matrix-glib-sdk 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with matrix-glib-sdk. If not, see
+ * .
+ */
+
+#ifndef __MATRIX_GLIB_SDK_MESSAGE_BASE_H__
+# define __MATRIX_GLIB_SDK_MESSAGE_BASE_H__
+
+# include
+# include
+
+G_BEGIN_DECLS
+
+# define MATRIX_MESSAGE_TYPE_BASE matrix_message_base_get_type()
+# define MATRIX_MESSAGE_BASE(o) (G_TYPE_CHECK_INSTANCE_CAST((o), MATRIX_MESSAGE_TYPE_BASE, MatrixMessageBase))
+# define MATRIX_MESSAGE_BASE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST((c), MATRIX_MESSAGE_TYPE_BASE, MatrixMessageBaseClass))
+# define MATRIX_MESSAGE_IS_BASE(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), MATRIX_MESSAGE_TYPE_BASE))
+# define MATRIX_MESSAGE_IS_BASE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE((c), MATRIX_MESSAGE_TYPE_BASE))
+# define MATRIX_MESSAGE_BASE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), MATRIX_MESSAGE_TYPE_BASE, MatrixMessageBaseClass))
+
+typedef struct _MatrixMessageBase MatrixMessageBase;
+typedef struct _MatrixMessageBaseClass MatrixMessageBaseClass;
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(MatrixMessageBase, g_object_unref)
+
+struct _MatrixMessageBase {
+ GObject parent_instance;
+};
+
+struct _MatrixMessageBaseClass {
+ GObjectClass parent_class;
+
+ void (*from_json)(MatrixMessageBase *message, JsonNode *json_data, GError **error);
+ void (*to_json)(MatrixMessageBase *message, JsonNode *json_data, GError **error);
+};
+
+GType matrix_message_base_get_type(void) G_GNUC_CONST;
+MatrixMessageBase* matrix_message_base_new_from_json(JsonNode *json_data, GError **error);
+void matrix_message_base_from_json(MatrixMessageBase *message, JsonNode* json_data, GError** error);
+void matrix_message_base_to_json(MatrixMessageBase *message, JsonNode *json_data, GError **error);
+MatrixMessageBase* matrix_message_base_construct(GType object_type);
+const gchar* matrix_message_base_get_message_type(MatrixMessageBase *message);
+void matrix_message_base_set_message_type(MatrixMessageBase *message, const gchar *message_type);
+const gchar* matrix_message_base_get_body(MatrixMessageBase *message);
+void matrix_message_base_set_body(MatrixMessageBase *message, const gchar *body);
+JsonNode* matrix_message_base_get_json(MatrixMessageBase *message, GError **error);
+
+GType matrix_message_get_handler(const gchar *message_type);
+void matrix_message_register_type(const gchar *message_type, GType message_gtype, GError **error);
+void matrix_message_unregister_type(const gchar *message_type);
+
+G_END_DECLS
+
+#endif /* __MATRIX_GLIB_SDK_MESSAGE_BASE_H__ */
diff --git a/src/matrix-message-base.vala b/src/matrix-message-base.vala
deleted file mode 100644
index 1cd2d7c..0000000
--- a/src/matrix-message-base.vala
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * This file is part of matrix-glib-sdk
- *
- * matrix-glib-sdk is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * matrix-glib-sdk 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with matrix-glib-sdk. If not, see
- * .
- */
-
-/**
- * Base class for message handlers.
- *
- * Message handler base class.
- */
-public abstract class Matrix.Message.Base : Object, Initable {
- /**
- * The message type.
- */
- public string? message_type { get; set; default = null; }
-
- /**
- * The body of the message.
- */
- public string? body { get; set; default = null; }
-
- public Json.Node? json {
- get {
- _json = new Json.Node(Json.NodeType.OBJECT);
- _json.set_object(new Json.Object());
-
- try {
- to_json(_json);
- } catch (Matrix.Error e) {
- return null;
- }
-
- return _json;
- }
-
- construct {
- if (value != null) {
- try {
- initialize_from_json(value);
- } catch (Matrix.Error e) {}
- }
- }
- }
-
- private bool _inited = false;
- private Error? _construct_error = null;
- private Json.Node? _json = null;
-
- public bool
- init(Cancellable? cancellable = null)
- throws GLib.Error
- {
- if (cancellable != null) {
- throw new Matrix.Error.UNSUPPORTED(
- "Cancellable initialization not supported");
- }
-
- if (_construct_error != null) {
- throw _construct_error;
- }
-
- _inited = true;
-
- return true;
- }
-
- private void
- initialize_from_json(Json.Node json_data)
- throws Matrix.Error
- {
- var gen = new Json.Generator();
- gen.set_root(json_data);
-
- if (json_data.get_node_type() != Json.NodeType.OBJECT) {
- throw new Matrix.Error.INVALID_FORMAT(
- "The message is not valid");
- }
-
- from_json(json_data);
- }
-
- public static Matrix.Message.Base?
- new_from_json(Json.Node json_data)
- throws Matrix.Error, GLib.Error
- {
- var root = json_data.get_object();
- Json.Node? node;
-
- if ((node = root.get_member("msgtype")) == null) {
- throw new Matrix.Error.INCOMPLETE(
- "Can not process a message without msgtype");
- }
-
- Type? msg_gtype;
-
- if ((msg_gtype = get_handler(node.get_string())) == null) {
- return null;
- }
-
- var ret = (Base)Object.new(msg_gtype,
- json : json_data);
-
- ret.init();
-
- return ret;
- }
-
- /**
- * Subclasses should override this method to initialize themselves
- * from JSON data while chaining up to allow parent classes to do
- * the same.
- */
- public virtual void
- from_json(Json.Node json_data)
- throws Matrix.Error
- {
- var root = json_data.get_object();
- Json.Node? node;
-
- if ((node = root.get_member("msgtype")) != null) {
- _message_type = node.get_string();
- } else if (Config.DEBUG) {
- warning("msgtype is not present in a message");
- }
-
- if ((node = root.get_member("body")) != null) {
- _body = node.get_string();
- } else if (Config.DEBUG) {
- warning("body is not presente in a message");
- }
- }
-
- /**
- * Subclasses should override this method to export their data to
- * JSON, while chaining up to allow parent classes to do the same.
- */
- public virtual void
- to_json(Json.Node json_data)
- throws Matrix.Error
- {
- var root = json_data.get_object();
-
- if (_message_type == null) {
- throw new Matrix.Error.INCOMPLETE(
- "Won't generate a message with an empty msgtype");
- }
-
- if (_body == null) {
- throw new Matrix.Error.INCOMPLETE(
- "Won't generate a message without body");
- }
-
- root.set_string_member("msgtype", _message_type);
- }
-}
-
-namespace Matrix.Message {
-
-private HashTable? type_handlers = null;
-
-/**
- * Get the {@link GLib.Type} of the class that is registered to handle
- * messages with type @param message_type.
- *
- * @param message_type the message type to look up
- * @return a {@link GLib.Type} or `null` if no handler is registered
- */
-public static GLib.Type?
-get_handler(string message_type)
-{
- unowned GLib.TypeClass? klass = null;
-
- if ((type_handlers != null)
- && ((klass = type_handlers.get(message_type)) != null)) {
- return klass.get_type();
- }
-
- if (Config.DEBUG) {
- warning("No registered type for message %s", message_type);
- }
-
- return null;
-}
-
-/**
- * Register @param message_type to be handled by the
- * type @param message_gtype.
- *
- * @param message_type the type of the message
- * @param message_gtype the {@link GLib.Type} of the event's handler
- */
-public static void
-register_type(string message_type, GLib.Type message_gtype)
- throws Matrix.Error
-{
- if (!message_gtype.is_a(typeof(Matrix.Message.Base))) {
- throw new Matrix.Error.INVALID_TYPE(
- "Invalid message handler type");
- }
-
- if (type_handlers == null) {
- type_handlers = new HashTable(
- str_hash, str_equal);
- }
-
- type_handlers.replace(message_type, message_gtype.class_ref());
-}
-
-/**
- * Unregister @param message_type.
- *
- * @param message_type the message type to remove
- */
-public void
-unregister_type(string message_type)
-{
- if (type_handlers != null) {
- type_handlers.remove(message_type);
- }
-}
-}
diff --git a/vapi/c-api.vapi b/vapi/c-api.vapi
index 93fc167..dfa316c 100644
--- a/vapi/c-api.vapi
+++ b/vapi/c-api.vapi
@@ -605,4 +605,41 @@ namespace Matrix {
throws Matrix.Error;
}
}
+
+ [CCode (gir_namespace = "MatrixMessage", gir_version = "0.0")]
+ namespace Message {
+ [CCode (cname = "matrix_message_get_handler")]
+ public static GLib.Type? get_handler(string message_type);
+
+ [CCode (cname = "matrix_message_register_type")]
+ public static void register_type(string message_type, GLib.Type message_gtype)
+ throws Matrix.Error;
+
+ [CCode (cname = "matrix_message_unregister_type")]
+ public void unregister_type(string message_type);
+
+ [CCode (cheader_filename = "matrix-message-base.h")]
+ public abstract class Base : GLib.Object, GLib.Initable {
+ public string? message_type { get; set; default = null; }
+ public string? body { get; set; default = null; }
+ public Json.Node? json { get; construct; }
+
+ public Base();
+
+ public bool init(GLib.Cancellable? cancellable = null)
+ throws GLib.Error;
+
+ private void initialize_from_json(Json.Node json_data)
+ throws Matrix.Error;
+
+ public static Matrix.Message.Base? new_from_json(Json.Node json_data)
+ throws Matrix.Error, GLib.Error;
+
+ public virtual void from_json(Json.Node json_data)
+ throws Matrix.Error;
+
+ public virtual void to_json(Json.Node json_data)
+ throws Matrix.Error;
+ }
+ }
}