Add text matching alogrithm function
authorSuyeon Hwang <stom.hwang@samsung.com>
Fri, 30 Jun 2017 08:54:41 +0000 (17:54 +0900)
committersooyeon.kim <sooyeon.kim@samsung.com>
Mon, 26 Mar 2018 12:46:38 +0000 (21:46 +0900)
Change-Id: I6f590f79ddeb42263650f55f2069a93472d7756d
Signed-off-by: Suyeon Hwang <stom.hwang@samsung.com>
client/CMakeLists.txt
common/vc_command_util.c [new file with mode: 0644]
common/vc_command_util.h [new file with mode: 0644]
include/voice_control_command_expand.h
server/CMakeLists.txt

index 074d790..fcc4c43 100644 (file)
@@ -4,6 +4,7 @@ SET(SRCS
        vc_dbus.c
        ../common/vc_cmd_db.c
        ../common/vc_command.c
+       ../common/vc_command_util.c
        ../common/vc_config_mgr.c
        ../common/vc_config_parser.c
        ../common/vc_info_parser.c
@@ -23,6 +24,7 @@ SET(WIDGET_SRCS
        vc_widget_dbus.c
        ../common/vc_cmd_db.c
        ../common/vc_command.c
+       ../common/vc_command_util.c
        ../common/vc_config_mgr.c
        ../common/vc_config_parser.c
        ../common/vc_info_parser.c
@@ -35,6 +37,7 @@ SET(MANAGER_SRCS
        vc_mgr_dbus.c
        ../common/vc_cmd_db.c
        ../common/vc_command.c
+       ../common/vc_command_util.c
        ../common/vc_config_mgr.c
        ../common/vc_config_parser.c
        ../common/vc_info_parser.c
diff --git a/common/vc_command_util.c b/common/vc_command_util.c
new file mode 100644 (file)
index 0000000..2386ef5
--- /dev/null
@@ -0,0 +1,420 @@
+/*
+* Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+*
+* Licensed under the Apache License, Version 2.0 (the License);
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* 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 "ctype.h"
+
+#include "vc_main.h"
+#include "vc_command_util.h"
+#include "vc_command.h"
+
+#include <voice_control_command_expand.h>
+
+vc_cmd_list_h ret_list = NULL;
+GList *split_list = NULL;
+int ret_cnt = -1;
+
+void __tolower(char *str)
+{
+       while (0 != *str) {
+               *str = tolower(*str);
+               ++str;
+       }
+}
+
+int vc_cmd_get_partially_matched_cmd_list(const char *command, vc_cmd_list_h src_list, vc_cmd_list_h dst_list, int search_level)
+{
+       SLOG(LOG_DEBUG, TAG_VCCMD, "@@@ vc_cmd_find_similar_commands");
+       if (NULL == command || NULL == src_list || NULL == dst_list || VC_SEARCH_TEXT_LEVEL > search_level || VC_SEARCH_PRON_LEVEL < search_level) {
+               SLOG(LOG_ERROR, TAG_VCCMD, "[ERROR] Invalid parameter");
+               return VC_ERROR_INVALID_PARAMETER;
+       }
+
+       char *temp_cmd = NULL;
+       char *split = NULL;
+       char *tok_ptr = NULL;
+
+       ret_list = dst_list;
+       split_list = NULL;
+       ret_cnt = 0;
+
+       temp_cmd = strdup(command);
+       if (NULL == temp_cmd) {
+               SLOG(LOG_ERROR, TAG_VCCMD, "[ERROR] Out of memory");
+               return VC_ERROR_OUT_OF_MEMORY;
+       }
+
+       __tolower(temp_cmd);
+       split = strtok_r(temp_cmd, " ", &tok_ptr);
+       while (NULL != split) {
+               char *temp = strdup(split);
+
+               if (NULL == temp) {
+                       SLOG(LOG_ERROR, TAG_VCCMD, "[ERROR] Out of memory");
+                       free(temp_cmd);
+                       temp_cmd = NULL;
+
+                       g_list_free_full(split_list, free);
+                       split_list = NULL;
+
+                       return VC_ERROR_OUT_OF_MEMORY;
+               }
+
+               SLOG(LOG_DEBUG, TAG_VCCMD, "(%s)", split);
+               split_list = g_list_append(split_list, temp);
+
+               split = strtok_r(NULL, " ", &tok_ptr);
+       }
+
+       free(temp_cmd);
+       temp_cmd = NULL;
+
+       int ret = 0;
+       ret = vc_search_text(src_list, search_level);
+
+       g_list_free_full(split_list, free);
+       split_list = NULL;
+       if (0 != ret) {
+               SLOG(LOG_ERROR, TAG_VCCMD, "[ERROR] Fail to search similar commands (%d)", ret);
+       }
+
+       SLOG(LOG_DEBUG, TAG_VCCMD, "@@@");
+       return ret;
+}
+
+int vc_search_text(vc_cmd_list_h list, int search_level)
+{
+       SLOG(LOG_DEBUG, TAG_VCCMD, "@@@ Full Text level matching");
+       vc_cmd_h vc_cmd = NULL;
+       int ret = VC_ERROR_NONE;
+
+       ret = vc_cmd_list_first(list);
+       if (0 != ret) {
+               SLOG(LOG_ERROR, TAG_VCCMD, "[ERROR] Fail to make list first (%d)", ret);
+               return ret;
+       }
+       ret = vc_cmd_list_get_current(list, &vc_cmd);
+       if (0 != ret) {
+               SLOG(LOG_ERROR, TAG_VCCMD, "[ERROR] Fail to get voice command handle (%d)", ret);
+               return ret;
+       }
+
+       while (NULL != vc_cmd) {
+               GList *item = NULL;
+               char *cmd_text = NULL;
+
+               if (0 != vc_cmd_get_command(vc_cmd, &cmd_text) || NULL == cmd_text) {
+                       SLOG(LOG_ERROR, TAG_VCCMD, "[ERROR] Fail to get command");
+                       return VC_ERROR_OPERATION_FAILED;
+               }
+
+               __tolower(cmd_text);
+               item = g_list_first(split_list);
+               while (NULL != item) {
+                       char *word = (char*)item->data;
+                       if (NULL != word && NULL == strstr(cmd_text, word)) {
+                               break;
+                       }
+
+                       item = item->next;
+               }
+
+               free(cmd_text);
+
+               if (NULL == item) {
+                       if (0 == vc_cmd_list_add(ret_list, vc_cmd)) {
+                               ret_cnt++;
+                       }
+               }
+
+               if (VC_ERROR_ITERATION_END == vc_cmd_list_next(list)) {
+                       /* Move commands from list to ret_list */
+                       if (0 != vc_cmd_list_first(ret_list) || 0 != vc_cmd_list_get_current(ret_list, &vc_cmd)) {
+                               break;
+                       }
+
+                       while (NULL != vc_cmd) {
+                               if (0 != vc_cmd_list_remove(list, vc_cmd)) {
+                                       break;
+                               }
+
+                               if (0 != vc_cmd_list_next(ret_list)) {
+                                       break;
+                               }
+
+                               if (0 != vc_cmd_list_get_current(ret_list, &vc_cmd)) {
+                                       break;
+                               }
+                       }
+
+                       break;
+               }
+
+               vc_cmd_list_get_current(list, &vc_cmd);
+       }
+
+       SLOG(LOG_DEBUG, TAG_VCCMD, "Number of matched text : (%d)", ret_cnt);
+
+       if (ret_cnt >= 1) {
+               return VC_ERROR_NONE;
+       }
+
+       if (VC_SEARCH_WORD_LEVEL > search_level) {
+               SLOG(LOG_DEBUG, TAG_VCCMD, "Text matching ends by search level (%d)", search_level);
+               return VC_ERROR_NONE;
+       }
+
+       return vc_search_word(list, search_level);
+}
+
+int vc_search_word(vc_cmd_list_h list, int search_level)
+{
+       SLOG(LOG_DEBUG, TAG_VCCMD, "@@@ Partial Word level matching");
+       vc_cmd_h vc_cmd = NULL;
+       vc_cmd_h ret_cmd = NULL;
+       int ret = VC_ERROR_NONE;
+       int max = -1;   // This is also threshold value
+
+       max = g_list_length(split_list) * VC_WORD_MATCHING_RATE;
+
+       ret = vc_cmd_list_first(list);
+       if (0 != ret) {
+               SLOG(LOG_ERROR, TAG_VCCMD, "[ERROR] Fail to make list first (%d)", ret);
+               return ret;
+       }
+       ret = vc_cmd_list_get_current(list, &vc_cmd);
+       if (0 != ret) {
+               SLOG(LOG_ERROR, TAG_VCCMD, "[ERROR] Fail to get voice command handle (%d)", ret);
+               return ret;
+       }
+
+       while (NULL != vc_cmd) {
+               GList *item = NULL;
+               char *cmd_text = NULL;
+               int cnt = 0;
+
+               if (0 != vc_cmd_get_command(vc_cmd, &cmd_text) || NULL == cmd_text) {
+                       SLOG(LOG_ERROR, TAG_VCCMD, "[ERROR] Fail to get command");
+                       return VC_ERROR_OPERATION_FAILED;
+               }
+
+               __tolower(cmd_text);
+               item = g_list_first(split_list);
+               while (NULL != item) {
+                       char *word = (char*)item->data;
+
+                       if (NULL != word && NULL != strstr(cmd_text, word)) {
+                               cnt++;
+                       }
+
+                       item = item->next;
+               }
+
+               free(cmd_text);
+
+               SLOG(LOG_DEBUG, TAG_VCCMD, "The number of matched word in the text : (%d)", cnt);
+               if (cnt > max) {
+                       ret_cmd = vc_cmd;
+                       max = cnt;
+               }
+
+               if (VC_ERROR_ITERATION_END == vc_cmd_list_next(list)) {
+                       break;
+               }
+
+               vc_cmd_list_get_current(list, &vc_cmd);
+       }
+
+       if (NULL != ret_cmd) {
+               if (0 != vc_cmd_list_remove(list, ret_cmd)) {
+                       return VC_ERROR_OPERATION_FAILED;
+               }
+               if (0 != vc_cmd_list_add(ret_list, ret_cmd)) {
+                       return VC_ERROR_OPERATION_FAILED;
+               }
+
+               ret_cnt++;
+
+               return VC_ERROR_NONE;
+       }
+
+       if (VC_SEARCH_CHAR_LEVEL > search_level) {
+               SLOG(LOG_DEBUG, TAG_VCCMD, "Text matching end by search level (%d)", search_level);
+               return VC_ERROR_NONE;
+       }
+
+       return vc_search_char(list, search_level);
+}
+
+/* check the behavior */
+int vc_search_char(vc_cmd_list_h list, int search_level)
+{
+       SLOG(LOG_DEBUG, TAG_VCCMD, "@@@ Partial Character level matching");
+       vc_cmd_h vc_cmd = NULL;
+       vc_cmd_h ret_cmd = NULL;
+       int threshold = 0;
+       GList *item = NULL;
+
+       int result_score = 2147483647;
+
+       vc_cmd_list_first(list);
+       vc_cmd_list_get_current(list, &vc_cmd);
+
+       while (NULL != vc_cmd) {
+               int score = 0;
+               char *split = NULL;
+               char *cmd_text = NULL;
+               char *tok_ptr = NULL;
+
+               if (0 != vc_cmd_get_command(vc_cmd, &cmd_text) || NULL == cmd_text) {
+                       SLOG(LOG_ERROR, TAG_VCCMD, "[ERROR] Fail to get command");
+                       return VC_ERROR_OPERATION_FAILED;
+               }
+
+               __tolower(cmd_text);
+               item = g_list_first(split_list);
+               while (NULL != item) {
+                       char *word = (char*)item->data;
+                       int min_score = -1;
+
+                       if (NULL == word || 3 > strlen(word)) {
+                               item = item->next;
+                               continue;
+                       }
+
+                       min_score = strlen(word) * VC_CHAR_MATCHING_RATE;       // This is also threshold
+
+                       split = strtok_r(cmd_text, " ", &tok_ptr);
+                       while (NULL != split) {
+                               SLOG(LOG_DEBUG, TAG_VCCMD, "(%s)", split);
+                               if (3 > strlen(split)) {
+                                       split = strtok_r(NULL, " ", &tok_ptr);
+                                       continue;
+                               }
+
+                               char *long_str = NULL;
+                               char *short_str = NULL;
+
+                               if (strlen(split) > strlen(word)) {
+                                       long_str = split;
+                                       short_str = word;
+                               } else {
+                                       long_str = word;
+                                       short_str = split;
+                               }
+
+                               int long_str_len = strlen(long_str);
+                               int short_str_len = strlen(short_str);
+
+                               int *cost = (int*)calloc(long_str_len + 1, sizeof(int));
+                               int *ncost = (int*)calloc(long_str_len + 1, sizeof(int));
+
+                               if (NULL == cost || NULL == ncost) {
+                                       SLOG(LOG_ERROR, TAG_VCCMD, "[ERROR] Out of memory");
+
+                                       free(cmd_text);
+                                       return VC_ERROR_OUT_OF_MEMORY;
+                               }
+
+                               for (int l = 0; l <= long_str_len; l++) {
+                                       cost[l] = l;
+                               }
+
+                               for (int s = 1; s <= short_str_len; s++) {
+                                       ncost[0] = s;
+
+                                       for (int l = 1; l <= long_str_len; l++) {
+                                               int match = (short_str[s - 1] == long_str[l - 1] ? 0 : 1);
+
+                                               int rep = cost[l - 1] + match;
+                                               int ins = cost[l] + 1;
+                                               int del = ncost[l - 1] + 1;
+
+                                               int min = (rep < ins ? rep : ins);
+                                               ncost[l] = (min < del ? min : del);
+                                       }
+
+                                       int *temp = cost;
+                                       cost = ncost;
+                                       ncost = temp;
+                               }
+
+                               if (min_score > cost[long_str_len]) {
+                                       min_score = cost[long_str_len];
+                               }
+
+                               free(cost);
+                               free(ncost);
+
+                               split = strtok_r(NULL, " ", &tok_ptr);
+                       }
+
+                       if (min_score < strlen(word) * VC_CHAR_MATCHING_RATE) {
+                               score = score + min_score;
+                       } else {
+                               score = score + strlen(word);
+                       }
+
+                       item = item->next;
+               }
+
+               free(cmd_text);
+               cmd_text = NULL;
+
+               if (result_score > score) {
+                       result_score = score;
+                       ret_cmd = vc_cmd;
+               }
+
+               if (VC_ERROR_ITERATION_END == vc_cmd_list_next(list)) {
+                       break;
+               }
+
+               vc_cmd_list_get_current(list, &vc_cmd);
+       }
+
+       item = g_list_first(split_list);
+       while (NULL != item) {
+               char *word = (char*)item->data;
+
+               if (NULL != word && 3 <= strlen(word)) {
+                       threshold += strlen(word);
+               }
+
+               item = item->next;
+       }
+
+       if (threshold > result_score) {
+               vc_cmd_list_remove(list, ret_cmd);
+               vc_cmd_list_add(ret_list, ret_cmd);
+               ret_cnt++;
+
+               return VC_ERROR_NONE;
+       }
+
+       if (VC_SEARCH_PRON_LEVEL > search_level) {
+               SLOG(LOG_DEBUG, TAG_VCCMD, "Text matching ends by search level (%d)", search_level);
+               return VC_ERROR_NONE;
+       }
+
+       return vc_search_pron(list, search_level);
+}
+
+int vc_search_pron(vc_cmd_list_h list, int search_level)
+{
+       SLOG(LOG_DEBUG, TAG_VCCMD, "@@@ Partial Pronunciation level matching");
+       return VC_ERROR_NONE;
+}
\ No newline at end of file
diff --git a/common/vc_command_util.h b/common/vc_command_util.h
new file mode 100644 (file)
index 0000000..a1a9e97
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+* Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+*
+* Licensed under the Apache License, Version 2.0 (the License);
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* 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.
+*/
+
+
+#ifndef __VOICE_CONTROL_COMMAND_UTIL_h_
+#define __VOICE_CONTROL_COMMAND_UTIL_h_
+
+#include <voice_control_command.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define VC_WORD_MATCHING_RATE 0.3
+#define VC_CHAR_MATCHING_RATE 0.25
+
+int vc_search_text(vc_cmd_list_h list, int search_level);
+int vc_search_word(vc_cmd_list_h list, int search_level);
+int vc_search_char(vc_cmd_list_h list, int search_level);
+int vc_search_pron(vc_cmd_list_h list, int search_level);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __VOICE_CONTROL_COMMAND_UTIL_h_ */
index 62d83ad..a8627db 100755 (executable)
@@ -42,6 +42,11 @@ typedef enum {
        VC_CMD_FORMAT_PARTIAL                   /**< Partial matched command */
 } vc_cmd_format_e;
 
+#define VC_SEARCH_TEXT_LEVEL 1
+#define VC_SEARCH_WORD_LEVEL 2
+#define VC_SEARCH_CHAR_LEVEL 3
+#define VC_SEARCH_PRON_LEVEL 4
+
 /**
 * @brief Sets command domain
 * @since_tizen @if MOBILE 2.4 @elseif WEARABLE 3.0 @endif
@@ -199,6 +204,27 @@ int vc_cmd_get_nlu_json(vc_cmd_h vc_cmd, char** json);
 */
 int vc_cmd_get_datetime(const char *text, time_t *result, char **remain);
 
+/**
+* @brief Gets the commands list of commands similar to the text
+* @since_tizen 4.0
+*
+* @param[in] command The text to find the similar commands
+* @param[in] src_list The command list handle of all registered foreground and widget type commands
+* @param[out] dst_list The commands list handle to similar commands
+* @param[in] search_level The search level selecting depth of algorithm
+*
+* @remark This function can modify @a src_list by the result, So if you want to keep the content of @a src_list,
+*      make a copy of @a src_list before running this function. And also, you need to create the list handle of
+*      @a dst_list before runnig this function, because this function does not create the handle internally.
+*
+* @return 0 on success, otherwise a negative error value
+* @retval #VC_ERROR_NONE Successful
+* @retval #VC_ERROR_OPERATION_FAILED operation failure
+* @retval #VC_ERROR_OUT_OF_MEMORY Not enough memory
+* @retval #VC_ERROR_INVALID_PARAMETER Invalid parameter
+*/
+int vc_cmd_get_partially_matched_cmd_list(const char *command, vc_cmd_list_h src_list, vc_cmd_list_h dst_list, int search_level);
+
 #ifdef __cplusplus
 }
 #endif
index f9e8d48..0065863 100644 (file)
@@ -1,5 +1,6 @@
 SET(SRCS
        ../common/vc_cmd_db.c
+       ../common/vc_command_util.c
        ../common/vc_command.c
        ../common/vc_config_mgr.c
        ../common/vc_config_parser.c