Bundles / src /

data.c

/*
 * Copyright (c) 2016 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <Eina.h>
#include <bundle.h>
#include <message_port.h>
#include "$(appName).h"
#include "data.h"
#include <efl_extension.h>

#define MESSAGE_PORT_RCV_NAME PACKAGE"_msg_port_local_rcv"

typedef struct data_callbacks {
	message_data_new_cb new_data_cb;
} data_callbacks_t;

static struct data_info {
	data_callbacks_t callbacks;
	Eina_List *bundles_received;
	bundle *bundle_obj;
	int msg_port_rcv_id;
} s_info = {
	.callbacks = {0,},
	.bundles_received = NULL,
	.bundle_obj = NULL,
	.msg_port_rcv_id = 0,
};

static void _header_iterator_cb(const char *key, const int type, const bundle_keyval_t *kv, void *user_data);
static void _bundle_clear_iterator_cb(const char *key, const int type, const bundle_keyval_t *kv, void *user_data);
static void _keys_iterator_cb(const char *key, const int type, const bundle_keyval_t *kv, void *user_data);
static void _message_received_cb(int local_port_id, const char *remote_app_id, const char *remote_port, bool trusted_remote_port, bundle *message, void *user_data);
static inline bundle *_get_bundle(int index);
static inline void _free_string_array(int count, char** keys);

/**
 * @brief Initialize functions for data module.
 * @param[in] new_data_cb Callback invoked when a new message is received. It is used to inform the view module about the message.
 */
void data_initialize(message_data_new_cb new_data_cb)
{
	s_info.callbacks.new_data_cb = new_data_cb;

	int ret = message_port_register_local_port(MESSAGE_PORT_RCV_NAME, _message_received_cb, NULL);
	if (ret < 0) {
		dlog_print(DLOG_ERROR, LOG_TAG, "[%s:%d] message_port_register_local_port() failed. Error: %s", __FILE__, __LINE__, get_error_message(ret));
		return;
	} else {
		s_info.msg_port_rcv_id = ret;
	}

	data_create_bundle();
}

/**
 * @brief Finalize function for data module.
 */
void data_finalize(void)
{
	data_destroy_bundle();

	/*
	 * all the s_info.items_list items shall be released
	 */
	if (s_info.bundles_received)
		s_info.bundles_received = eina_list_free(s_info.bundles_received);

	if (s_info.msg_port_rcv_id > 0)
		message_port_unregister_local_port(s_info.msg_port_rcv_id);
}

/**
 * @brief Function creates a bundle handle, adds a header and fills it with relevant data.
 * If the bundle handle already exists, then it is destroyed and a new one is created.
 * The newly created bundle handle is stored internally.
 * @return The function returns 'true' if the bundle handle is created successfully,
 * otherwise 'false' is returned.
 */
bool data_create_bundle(void)
{
	s_info.bundle_obj = bundle_create();
	if (!s_info.bundle_obj) {
		dlog_print(DLOG_ERROR, LOG_TAG, "bundle_create() failed.");
		return false;
	}

	return true;
}

/**
 * @brief Function destroys a bundle handle.
 * If the bundle handle does not exist, then it nothing happens.
 */
void data_destroy_bundle(void)
{
	int ret;

	if (!s_info.bundle_obj)
		return;

	ret = bundle_free(s_info.bundle_obj);
	if (ret != BUNDLE_ERROR_NONE) {
		dlog_print(DLOG_ERROR, LOG_TAG, "bundle_free() failed. Err = %d.", ret);
		return;
	}

	s_info.bundle_obj = NULL;
}

/**
 * @brief Function which adds a numeric value to the bundle object.
 * If a bundle handle is not prior created, the function fails.
 * @param[in] key The key name for value identification.
 * @param[in] value The value assigned to the given key.
 * @return The function returns 'true' if the value is added successfully,
 * otherwise 'false' is returned.
 */
bool data_add_byte(const char *key, int value)
{
	int ret;

	data_delete_key(-1, key);

	ret = bundle_add_byte(s_info.bundle_obj, key, &value, sizeof(value));
	if (ret != BUNDLE_ERROR_NONE) {
		dlog_print(DLOG_ERROR, LOG_TAG, "_add_byte() failed. Err = %s.", get_error_message(ret));
		return false;
	}

	return true;
}

/**
 * @brief Function which adds a string value to the bundle object.
 * If a bundle handle is not prior created, the function fails.
 * @param[in] key The key name for value identification.
 * @param[in] value The value assigned to the given key.
 * @return The function returns 'true' if the value is added successfully,
 * otherwise 'false' is returned.
 */
bool data_add_string(const char *key, const char *value)
{
	int ret;

	data_delete_key(-1, key);

	ret = bundle_add_str(s_info.bundle_obj, key, value);
	if (ret != BUNDLE_ERROR_NONE) {
		dlog_print(DLOG_ERROR, LOG_TAG, "_add_string() failed. Err = %s.", get_error_message(ret));
		return false;
	}

	return true;
}

/**
 * @brief Internal function that creates a key-value pair of the string array type. The array is then filled with key names and the data types.
 * @param[in] count Number of keys stored in a bundle
 * @return true on success or false on fail.
 */
static bool _add_header_data(int count)
{
	int ret;
	char** keys = calloc(count, sizeof(char*));

	data_delete_key(-1, BUNDLE_HEADER_DATA_KEY);
	bundle_foreach(s_info.bundle_obj, _header_iterator_cb, (void*)keys);

	ret = bundle_add_str_array(s_info.bundle_obj, BUNDLE_HEADER_DATA_KEY, (const char**)keys, count);
	if (ret != BUNDLE_ERROR_NONE) {
		dlog_print(DLOG_ERROR, LOG_TAG, "_add_string() failed. Err = %s.", get_error_message(ret));

		_free_string_array(count, keys);
		return false;
	}

	_free_string_array(count, keys);
	return true;
}

/**
 * @brief Creates a header pair containing the count of the remaining pairs
 * @return true on success or false on fail.
 */
bool data_add_header(void)
{
	int count = 0;
	int ret = 0;

	bundle_del(s_info.bundle_obj, BUNDLE_HEADER_KEY);
	bundle_del(s_info.bundle_obj, BUNDLE_HEADER_DATA_KEY);

	count = bundle_get_count(s_info.bundle_obj);

	if (!_add_header_data(count)) {
		dlog_print(DLOG_ERROR, LOG_TAG, "[%s:%d] Function _add_header_data() failed", __FILE__, __LINE__);
		return false;
	}

	ret = bundle_add_byte(s_info.bundle_obj, BUNDLE_HEADER_KEY, (void *)&count, sizeof(int));
	if (ret != BUNDLE_ERROR_NONE) {
		dlog_print(DLOG_ERROR, LOG_TAG, "[%s:%d] bundle_add_byte() error: %s", __FILE__, __LINE__, get_error_message(ret));
		return false;
	}

	return true;
}

/**
 * @brief Check if the given key exist in the bundle with the given index.
 * @param[in] index Bundle's index (>=0 bundles received in a message, <0 bundle prepared using the source view).
 * @param[in] key The key to search for.
 * @return true - key exists, false the key doesn't exist.
 */
bool data_key_exists(int index, const char *key)
{
	bundle* bun = _get_bundle(index);
	bundle_get_type(bun, key);
	return !(get_last_result() == BUNDLE_ERROR_KEY_NOT_AVAILABLE);
}

/**
 * @brief Check whether the given key contains a byte or a string value.
 * @param[in] index The bundle index.
 * @param[in] key The key name.
 * @return false - value type is byte, false - otherwise.
 */
int data_is_byte(int index, const char *key)
{
	bundle* bun = _get_bundle(index);
	set_last_result(BUNDLE_ERROR_NONE);
	int type = bundle_get_type(bun, key);
	int ret = get_last_result();
	if (ret != BUNDLE_ERROR_NONE) {
		dlog_print(DLOG_ERROR, LOG_TAG, "[%s:%d] bundle_get_type error: %s", __FILE__, __LINE__, get_error_message(ret));
		return 0;
	}

	return (type == BUNDLE_TYPE_BYTE);
}

/**
 * @brief Function returns a text with the key's type.
 * @param[in] index The bundle index.
 * @param[in] key The key name.
 * @return String containing the value's type name (e.g. "byte", "string").
 */
char *data_get_type_text(int index, const char *key)
{
	int type;
	int ret;
	bundle* bun = _get_bundle(index);

	set_last_result(BUNDLE_ERROR_NONE);
	type = bundle_get_type(bun, key);
	ret = get_last_result();
	if (ret != BUNDLE_ERROR_NONE) {
		dlog_print(DLOG_ERROR, LOG_TAG, "[%s:%d] bundle_get_type error: %s", __FILE__, __LINE__, get_error_message(ret));
		return NULL;
	}

	if (type == BUNDLE_TYPE_BYTE)
		return "byte";
	else
		return "string";
}

/**
 * @brief Function returns the number of key-value pairs in the given bundle.
 * @param[in] index The bundle's index.
 * @return Number of key-value pairs stored in a bundle.
 */
int data_get_count(int index)
{
	bundle* bun = _get_bundle(index);
	return bundle_get_count(bun);
}

/**
 * @brief Function returns a byte value associated with given key stored in a bundle under given index.
 * @param[in] index The bundle index.
 * @param[in] key The key name.
 * @return The value stored in a bundle under provided index and associated with given key.
 */
int data_get_byte(int index, const char *key)
{
	int *val = NULL;
	size_t size = 0;
	bundle *bun = _get_bundle(index);

	int ret = bundle_get_byte(bun, key, (void**)&val, &size);
	if (ret != BUNDLE_ERROR_NONE) {
		dlog_print(DLOG_ERROR, LOG_TAG, "[%s:%d] bundle_get_byte() error: %s", __FILE__, __LINE__, get_error_message(ret));
		return 0;
	}

	return *val;
}

/**
 * @brief Function returns a string value associated with given key stored in a bundle under given index.
 * @param[in] index The bundle index.
 * @param[in] key The key name.
 * @return The value stored in a bundle under provided index and associated with given key.
 */
char *data_get_string(int index, const char *key)
{
	char *str = NULL;
	bundle *bun = _get_bundle(index);

	int ret = bundle_get_str(bun, key, &str);
	if (ret != BUNDLE_ERROR_NONE) {
		dlog_print(DLOG_ERROR, LOG_TAG, "[%s:%d] data_get_string() error: %s", __FILE__, __LINE__, get_error_message(ret));
		return NULL;
	}

	return str;
}

/**
 * @brief Function returns a byte value associated with given key stored in a bundle under given index.
 * @param[in] index The bundle index.
 * @param[in] key The key name.
 * @param[out] len Length of the string array
 * @return The value stored in a bundle under provided index and associated with given key.
 */
const char **data_get_string_array(int index, const char *key, int *len)
{
	int ret;
	const char **values = NULL;
	bundle *bun = _get_bundle(index);

	values = bundle_get_str_array(bun, key, len);
	ret = get_last_result();
	if (ret != BUNDLE_ERROR_NONE) {
		dlog_print(DLOG_ERROR, LOG_TAG, "[%s:%d] bundle_get_str_array() error: %s", __FILE__, __LINE__, get_error_message(ret));
		return NULL;
	}

	return values;
}

/**
 * @brief Functions deletes the given key-value pair from the bundle.
 * @param[in] index bundle's index.
 * @param[in] key Key to remove.
 */
void data_delete_key(int index, const char *key)
{
	int ret;
	bundle *bun = _get_bundle(index);

	ret = bundle_del(bun, key);
	if (ret != BUNDLE_ERROR_NONE && ret != BUNDLE_ERROR_KEY_NOT_AVAILABLE) {
		dlog_print(DLOG_ERROR, LOG_TAG, "[%s:%d] bundle_del() error: %s", __FILE__, __LINE__, get_error_message(ret));
		return;
	}
}

/**
 * @brief Function removes all key-value pairs from a bundle.
 */
void data_clear_bundle(void)
{
	bundle_foreach(s_info.bundle_obj, _bundle_clear_iterator_cb, NULL);
}

Eina_List *data_get_keys(int index)
{
	bundle *bun = _get_bundle(index);
	Eina_List *keys = NULL;
	bundle_foreach(bun, _keys_iterator_cb, (void*)&keys);

	return keys;
}

/**
 * @brief Function sends a message containing the bundle data.
 * @return true on success or false on fail.
 */
bool data_send_message(void)
{
	int ret = message_port_send_message(PACKAGE, MESSAGE_PORT_RCV_NAME, s_info.bundle_obj);
	if (ret != MESSAGE_PORT_ERROR_NONE) {
		dlog_print(DLOG_ERROR, LOG_TAG, "message_port_send_message() failed. Err = %s.", get_error_message(ret));
		return false;
	}

	return true;
}

/**
 * @brief Internal function used to retrieve a bundle with the given index.
 * @param[in] index Index of a bundle object to be returned.
 * @return A bundle object. If a non-negative index value is provided, then a relevant bundle object, received via Message Port, is returned. For negative index value, the bundle object prepared for sending is returned.
 */
static inline bundle *_get_bundle(int index)
{
	bundle *bun = NULL;

	if (index < 0)
		bun = s_info.bundle_obj;
	else
		bun = eina_list_nth(s_info.bundles_received, index);

	return bun;
}

/**
 * @brief Internal callback function invoked by a bundle iterator. It is used to delete all the key-value pairs from the given bundle.
 * @param[in] key Current key.
 * @param[in] type Value type.
 * @param[in] kv Bundle value container.
 * @param[in] user_data User data.
 */
static void _bundle_clear_iterator_cb(const char *key, const int type, const bundle_keyval_t *kv, void *user_data)
{
	bundle_del(s_info.bundle_obj, key);
}

/**
 * @brief Internal callback function invoked by a bundle iterator.  It is used to fill the header data array.
 * @param[in] key Current key.
 * @param[in] type Value type.
 * @param[in] kv Bundle value container.
 * @param[out] user_data The array to store the keys.
 */
static void _header_iterator_cb(const char *key, const int type, const bundle_keyval_t *kv, void *user_data)
{
	char *type_name = NULL;
	char **keys = (char**)user_data;
	char buf[NAME_MAX];
	static int iter = 0;
	int count = bundle_get_count(s_info.bundle_obj);

	if (type == BUNDLE_TYPE_BYTE)
		type_name = "byte";
	else
		type_name = "string";

	snprintf(buf, NAME_MAX, "%s\2%s", key, type_name);
	keys[iter] = strdup(buf);

	if (iter < count -1)
		iter++;
	else
		iter = 0;
}

/**
 * @brief Internal callback function invoked by a bundle iterator used to create an eina_list for all of the available keys from the given bundle storage.
 * @param[in] key Current key.
 * @param[in] type Value type.
 * @param[in] kv Bundle value container.
 * @param[out] user_data List to store the keys.
 */
static void _keys_iterator_cb(const char *key, const int type, const bundle_keyval_t *kv, void *user_data)
{
	Eina_List **keys = (Eina_List **)user_data;
	*keys = eina_list_append(*keys, strdup(key));
}

/**
 * @brief Internal callback function invoked when a message is received. The bundle received is appended to an eina_list. This callback function is invoked to inform the view module that the message had just been received.
 * @param[in] local_port_id Local port id
 * @param[in] remote_app_id Remote app id (In this case "org.example.bundles")
 * @param[in] remote_port Remote port name
 * @param[in] trusted_remote_port true - the port is trusted.
 * @param[in] message The message received.
 * @param[in] user_data User data.
 */
static void _message_received_cb(int local_port_id, const char *remote_app_id, const char *remote_port, bool trusted_remote_port, bundle *message, void *user_data)
{
	bundle *message_cpy = bundle_dup(message);
	if (!message_cpy) {
		dlog_print(DLOG_ERROR, LOG_TAG, "[%s:%d] message_cpy == NULL", __FILE__, __LINE__);
		return;
	}

	s_info.bundles_received = eina_list_append(s_info.bundles_received, (void*)message_cpy);
	s_info.callbacks.new_data_cb(eina_list_count(s_info.bundles_received) - 1);
}

/**
 * @brief Internal function that frees a string array and all of its content.
 * @param[in] count The array length.
 * @param[in] keys The array to free.
 */
static inline void _free_string_array(int count, char** keys)
{
	int i;
	for (i = 0; i < count; ++i)
		free(keys[i]);

	free(keys);
}