Port MatrixMessageBase to C
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -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 | ||||
|   | ||||
| @@ -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 \ | ||||
|   | ||||
							
								
								
									
										676
									
								
								src/matrix-message-base.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										676
									
								
								src/matrix-message-base.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,676 @@ | ||||
| /* | ||||
|  * 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 | ||||
|  * <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| #include "matrix-message-base.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); | ||||
| } | ||||
|  | ||||
| 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); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										66
									
								
								src/matrix-message-base.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/matrix-message-base.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
|  * <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| #ifndef __MATRIX_GLIB_SDK_MESSAGE_BASE_H__ | ||||
| # define __MATRIX_GLIB_SDK_MESSAGE_BASE_H__ | ||||
|  | ||||
| # include <glib-object.h> | ||||
| # include <json-glib/json-glib.h> | ||||
|  | ||||
| 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 *matrix_message_base, JsonNode *json_data, GError **error); | ||||
|     void (*to_json)(MatrixMessageBase *matrix_message_base, 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__ */ | ||||
| @@ -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 | ||||
|  * <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * 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<string, TypeClass>? 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<string, GLib.TypeClass>( | ||||
|                 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); | ||||
|     } | ||||
| } | ||||
| } | ||||
| @@ -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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user