diff --git a/src/obiview.c b/src/obiview.c index 494bdb3..a73e4f8 100644 --- a/src/obiview.c +++ b/src/obiview.c @@ -38,6 +38,7 @@ #include "linked_list.h" #include "utils.h" #include "obiblob.h" +#include "libjson/json_utils.h" #define DEBUG_LEVEL 0 // TODO has to be defined somewhere else (cython compil flag?) @@ -122,24 +123,6 @@ static size_t get_platform_view_file_size(); static int enlarge_view_file(Obiview_p view, size_t new_size); -/** - * @brief Internal function writing new comments in a view file. - * - * The new comments are concatenated to the pre-existing ones. - * The view file is enlarged if necessary. - * - * @param view A pointer on the view. - * @param comments The new comments that should be added. - * - * @retval 0 if the operation was successfully completed. - * @retval -1 if an error occurred. - * - * @since August 2016 - * @author Celine Mercier (celine.mercier@metabarcoding.org) - */ -static int write_comments_to_view_file(Obiview_p view, const char* comments); - - /** * @brief Internal function creating a file containing all the informations on a view. * @@ -490,14 +473,15 @@ static char* view_check_one_predicate(Obiview_p view, char* (*predicate_function * @brief Internal function checking all the predicates associated with a view. * * @param view The view. + * @param write Whether the verified predicates should be written in the view comments. * - * @returns A character string describing all the predicates separated by line breaks. - * @retval NULL if at least one of the predicates is false or if there was an error. + * @retval 0 if the operation was successfully completed. + * @retval -1 if at least one of the predicates is false or if there was an error. * * @since July 2016 * @author Celine Mercier (celine.mercier@metabarcoding.org) */ -static char* view_check_all_predicates(Obiview_p view); +static int view_check_all_predicates(Obiview_p view, bool write); /************************************************************************ @@ -694,30 +678,6 @@ static int enlarge_view_file(Obiview_p view, size_t new_size) } -static int write_comments_to_view_file(Obiview_p view, const char* comments) -{ - size_t new_size; - - if (comments == NULL) - return 0; // TODO or error? discuss - - new_size = (view->infos)->used_size + strlen(comments); - - // Check if the file has to be enlarged - if (new_size >= (view->infos)->file_size) - { - if (enlarge_view_file(view, new_size) < 0) - return -1; - } - - strcat((view->infos)->comments, comments); - - (view->infos)->used_size = new_size; - - return 0; -} - - static int create_obiview_file(OBIDMS_p dms, const char* view_name) { char* file_name; @@ -1067,7 +1027,6 @@ static int rename_finished_view(Obiview_p view) static int finish_view(Obiview_p view) { - char* predicates; int i; OBIDMS_column_p column; @@ -1100,16 +1059,11 @@ static int finish_view(Obiview_p view) } // Check predicates - predicates = view_check_all_predicates(view); - if (predicates == NULL) + if (view_check_all_predicates(view, true) < 0) { - obidebug(1, "\nView predicates not respected"); - return -1; // TODO reverse view (delete files) - } - else - { - write_comments_to_view_file(view, predicates); - free(predicates); + obidebug(1, "\nView predicates not respected, view rollbacked"); + obi_rollback_view(view); // TODO discuss, maybe never call from C layer + return -1; } if (save_view(view) < 0) @@ -1419,9 +1373,9 @@ static char* view_check_qual_match_seqs(Obiview_p view) if ((qual != OBIQual_int_NA) && (seq != OBISeq_NA)) { // Test that the lengths of the quality and the sequence are equal - if (qual_len != (int)strlen(seq)) + if ((size_t)qual_len != strlen(seq)) { - obidebug(1, "\nError checking the predicate for view %s: The sequences and sequence quality arrays match. \n%d, %s, %d", (view->infos)->name, j, seq, qual_len); + obidebug(1, "\nError checking the predicate for view %s: The sequences and sequence quality arrays match.", (view->infos)->name); return NULL; } } @@ -1471,78 +1425,37 @@ static char* view_check_one_predicate(Obiview_p view, char* (*predicate_function } -static char* view_check_all_predicates(Obiview_p view) +static int view_check_all_predicates(Obiview_p view, bool write) { - int i, j; - size_t size_to_allocate; + int i; char* predicate = NULL; - char* all_predicates_string = NULL; - char** all_predicates = NULL; - - if (view->nb_predicates == 0) - { - all_predicates_string = (char*) malloc(1*sizeof(char)); - strcpy(all_predicates_string, ""); - return all_predicates_string; - } - - size_to_allocate = 0; - - // Allocate memory for predicate array - all_predicates = (char**) malloc((view->nb_predicates) * sizeof(char*)); - if (all_predicates == NULL) - { - obi_set_errno(OBI_MALLOC_ERROR); - obidebug(1, "\nError allocating memory for predicate array."); - return NULL; - } for (i=0; i < view->nb_predicates; i++) { - // Initialize predicate in predicate array - all_predicates[i] = NULL; - // Check the predicate + // Check predicate predicate = view_check_one_predicate(view, (view->predicate_functions)[i]); if (predicate == NULL) { - // TODO discuss - for (j=0; j<=i; j++) - free(all_predicates[j]); - free(all_predicates); - return NULL; + // TODO discuss what to do + return -1; } else { - all_predicates[i] = predicate; - size_to_allocate = size_to_allocate + strlen(predicate) + 1; // +1 for '\n' + if ((write) && (predicate[0]!='\0')) + { + // Add predicate in comments + if (obi_view_add_comment(view, PREDICATE_KEY, predicate) < 0) + { + obi_set_errno(OBIVIEW_ERROR); + obidebug(1, "\nError adding a verified predicate (%s) in the comments of a view.", predicate); + free(predicate); + return -1; + } + } + free(predicate); } } - - size_to_allocate += 1; // +1 for '\0' - - all_predicates_string = (char*) malloc(size_to_allocate * sizeof(char)); - if (all_predicates_string == NULL) - { - obi_set_errno(OBI_MALLOC_ERROR); - obidebug(1, "\nError allocating memory for predicate character string."); - return NULL; - } - - // Build the character string with all the predicates - strcpy(all_predicates_string, all_predicates[0]); - for (i=1; i < view->nb_predicates; i++) - { - if (strlen(all_predicates_string)) // First predicate not empty - strcat(all_predicates_string, "\n"); - strcat(all_predicates_string, all_predicates[i]); - } - - // Free everything - for (i=0; i < view->nb_predicates; i++) - free(all_predicates[i]); - free(all_predicates); - - return all_predicates_string; + return 0; } @@ -1561,8 +1474,8 @@ Obiview_p obi_new_view(OBIDMS_p dms, const char* view_name, Obiview_p view_to_cl Obiview_p view; int i; index_t line_nb; - char* clone_comment; OBIDMS_column_p column; + int comments_ok; // Check that the DMS is a valid pointer if (dms == NULL) @@ -1708,27 +1621,6 @@ Obiview_p obi_new_view(OBIDMS_p dms, const char* view_name, Obiview_p view_to_cl // Fill informations strcpy((view->infos)->view_type, (view_to_clone->infos)->view_type); strcpy((view->infos)->created_from, (view_to_clone->infos)->name); - - // Write comment specifying the name of the cloned view - clone_comment = (char*) malloc((strlen((view_to_clone->infos)->name) + 15)*sizeof(char)); - if (clone_comment == NULL) - { - obi_set_errno(OBI_MALLOC_ERROR); - obidebug(1, "\nError allocating memory for a view comment"); - return NULL; - } - if (sprintf(clone_comment, "Cloned from %s.\n", (view_to_clone->infos)->name) < 0) - { - obi_set_errno(OBIVIEW_ERROR); - obidebug(1, "\nError building a view comment"); - return NULL; - } - if (write_comments_to_view_file(view, clone_comment) < 0) - { - obidebug(1, "\nError writing comments when creating a view"); - close_view(view); - return NULL; - } } // Else, fill empty view structure @@ -1759,13 +1651,29 @@ Obiview_p obi_new_view(OBIDMS_p dms, const char* view_name, Obiview_p view_to_cl (view->predicate_functions)[0] = view_check_qual_match_seqs; // Write comments - if (write_comments_to_view_file(view, comments) < 0) + // Comments must be a json string, even empty + if ((strcmp(comments, "") == 0) || (comments == NULL)) + comments_ok = obi_view_write_comments(view, "{}"); + else + comments_ok = obi_view_write_comments(view, comments); + if (comments_ok < 0) { obidebug(1, "\nError writing comments when creating a view"); close_view(view); return NULL; } + // Add the comment specifying the name of the cloned view if there is one + if (view_to_clone != NULL) + { + if (obi_view_add_comment(view, "Cloned from", (view_to_clone->infos)->name) < 0) + { + obidebug(1, "\nError adding comment about cloned view when creating a view"); + close_view(view); + return NULL; + } + } + // Store reference for line selection if (view->line_selection == NULL) { @@ -2524,6 +2432,69 @@ int obi_view_create_column_alias(Obiview_p view, const char* current_name, const } +int obi_view_write_comments(Obiview_p view, const char* comments) +{ + size_t new_size; + + // Check that the view is not read-only + if (view->read_only) + { + obi_set_errno(OBIVIEW_ERROR); + obidebug(1, "\nError trying to write comments in a read-only view"); + return -1; + } + + if (comments == NULL) + return 0; // TODO or error? discuss + + new_size = sizeof(Obiview_infos_t) + strlen(comments) + 1; + + // Check if the file has to be enlarged + if (new_size >= (view->infos)->file_size) + { + if (enlarge_view_file(view, new_size) < 0) + return -1; + } + + strcpy((view->infos)->comments, comments); + + (view->infos)->used_size = new_size; + + return 0; +} + + +int obi_view_add_comment(Obiview_p view, const char* key, const char* value) +{ + char* new_comments = NULL; + + // Check that the view is not read-only + if (view->read_only) + { + obi_set_errno(OBIVIEW_ERROR); + obidebug(1, "\nError trying to add a comment to a read-only view"); + return -1; + } + + new_comments = obi_add_comment((view->infos)->comments, key, value); + if (new_comments == NULL) + { + obidebug(1, "\nError adding a comment to a view, key: %s, value: %s", key, value); + return -1; + } + + if (obi_view_write_comments(view, new_comments) < 0) + { + obidebug(1, "\nError adding a comment to a view, key: %s, value: %s", key, value); + return -1; + } + + free(new_comments); + + return 0; +} + + int obi_save_and_close_view(Obiview_p view) { // Finish and save the view if it is not read-only diff --git a/src/obiview.h b/src/obiview.h index 05ea843..b3dd49c 100644 --- a/src/obiview.h +++ b/src/obiview.h @@ -59,6 +59,8 @@ */ #define ID_PREFIX "seq" /**< The default prefix of sequence identifiers in automatic ID columns. */ +#define PREDICATE_KEY "predicates" /**< The key used in the json-formatted view comments to store predicates. + */ /** @@ -484,6 +486,44 @@ OBIDMS_column_p* obi_view_get_pointer_on_column_in_view(Obiview_p view, const ch int obi_view_create_column_alias(Obiview_p view, const char* current_name, const char* alias); +/** + * @brief Internal function writing new comments in a view file. + * + * The new comments replace the pre-existing ones. + * The view file is enlarged if necessary. + * + * @param view A pointer on the view. + * @param comments The new comments that should be written. + * + * @retval 0 if the operation was successfully completed. + * @retval -1 if an error occurred. + * + * @since August 2016 + * @author Celine Mercier (celine.mercier@metabarcoding.org) + */ +int obi_view_write_comments(Obiview_p view, const char* comments); + + +/** + * @brief Adds comments to a view file. + * + * This reads the comments in the JSON format and adds the key value pair. + * If the key already exists, the value format is turned into an array and the new value is appended + * if it is not already in the array. + * + * @param column A pointer on an OBIDMS column. + * @param key The key. + * @param value The value associated with the key. + * + * @retval 0 if the operation was successfully completed. + * @retval -1 if an error occurred. + * + * @since August 2018 + * @author Celine Mercier (celine.mercier@metabarcoding.org) + */ +int obi_view_add_comment(Obiview_p view, const char* key, const char* value); + + /** * @brief Closes an opened view, and saves it if it is not read-only (meaning it is not already saved in the view file). *