rjw | 1f88458 | 2022-01-06 17:20:42 +0800 | [diff] [blame^] | 1 | #include "AudioParamParserPriv.h" |
| 2 | |
| 3 | EXPORT ParamTreeView *paramTreeViewCreate(AudioType *audioType, int verMaj, int verMin) |
| 4 | { |
| 5 | ParamTreeView *paramTreeView = malloc(sizeof(ParamTreeView)); |
| 6 | paramTreeView->audioType = audioType; |
| 7 | paramTreeView->verMaj = verMaj; |
| 8 | paramTreeView->verMin = verMin; |
| 9 | paramTreeView->treeRootHash = NULL; |
| 10 | return paramTreeView; |
| 11 | } |
| 12 | |
| 13 | EXPORT void paramTreeViewRelease(ParamTreeView *paramTreeView) |
| 14 | { |
| 15 | if (paramTreeView) |
| 16 | { |
| 17 | if (paramTreeView->treeRootHash) |
| 18 | { |
| 19 | TreeRoot *tmp, *item; |
| 20 | HASH_ITER(hh, paramTreeView->treeRootHash, item, tmp) |
| 21 | { |
| 22 | HASH_DEL(paramTreeView->treeRootHash, item); |
| 23 | treeRootRelease(item); |
| 24 | } |
| 25 | } |
| 26 | free(paramTreeView); |
| 27 | } |
| 28 | } |
| 29 | |
| 30 | EXPORT TreeRoot *treeRootCreate(const char *name, xmlNode *treeRootNode, ParamTreeView *paramTreeView) |
| 31 | { |
| 32 | TreeRoot *treeRoot = malloc(sizeof(TreeRoot)); |
| 33 | treeRoot->name = strdup(name); |
| 34 | treeRoot->treeRootNode = treeRootNode; |
| 35 | treeRoot->paramTreeView = paramTreeView; |
| 36 | treeRoot->featureHash = NULL; |
| 37 | treeRoot->switchFieldInfo = NULL; |
| 38 | return treeRoot; |
| 39 | } |
| 40 | |
| 41 | EXPORT void treeRootRelease(TreeRoot *treeRoot) |
| 42 | { |
| 43 | if (treeRoot) |
| 44 | { |
| 45 | if (treeRoot->featureHash) |
| 46 | { |
| 47 | Feature *tmp, *item; |
| 48 | HASH_ITER(hh, treeRoot->featureHash, item, tmp) |
| 49 | { |
| 50 | HASH_DEL(treeRoot->featureHash, item); |
| 51 | featureRelease(item); |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | free(treeRoot->name); |
| 56 | free(treeRoot); |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | EXPORT Feature *featureCreate(const char *name, AudioType *audioType, FieldInfo *switchFieldInfo, const char *featureOption) |
| 61 | { |
| 62 | Feature *feature = malloc(sizeof(Feature)); |
| 63 | feature->name = strdup(name); |
| 64 | feature->audioType = audioType; |
| 65 | |
| 66 | if (featureOption) |
| 67 | { |
| 68 | feature->featureOption = strdup(featureOption); |
| 69 | } |
| 70 | else |
| 71 | { |
| 72 | feature->featureOption = NULL; |
| 73 | } |
| 74 | |
| 75 | feature->categoryPathHash = NULL; |
| 76 | feature->featureFieldHash = NULL; |
| 77 | feature->switchFieldInfo = switchFieldInfo; |
| 78 | return feature; |
| 79 | } |
| 80 | |
| 81 | EXPORT void featureRelease(Feature *feature) |
| 82 | { |
| 83 | if (feature) |
| 84 | { |
| 85 | if (feature->categoryPathHash) |
| 86 | { |
| 87 | CategoryPath *tmp, *item; |
| 88 | HASH_ITER(hh, feature->categoryPathHash, item, tmp) |
| 89 | { |
| 90 | HASH_DEL(feature->categoryPathHash, item); |
| 91 | categoryPathRelease(item); |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | if (feature->featureFieldHash) |
| 96 | { |
| 97 | FeatureField *tmp, *item; |
| 98 | HASH_ITER(hh, feature->featureFieldHash, item, tmp) |
| 99 | { |
| 100 | HASH_DEL(feature->featureFieldHash, item); |
| 101 | featureFieldRelease(item); |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | if (feature->name) |
| 106 | { |
| 107 | free(feature->name); |
| 108 | } |
| 109 | |
| 110 | if (feature->featureOption) |
| 111 | { |
| 112 | free(feature->featureOption); |
| 113 | } |
| 114 | |
| 115 | free(feature); |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | EXPORT APP_STATUS categoryPathValidation(CategoryPath *categoryPath) |
| 120 | { |
| 121 | char *path; |
| 122 | char *categoryGroup; |
| 123 | |
| 124 | if (!strcmp(categoryPath->path, "")) |
| 125 | { |
| 126 | return APP_NO_ERROR; |
| 127 | } |
| 128 | |
| 129 | path = strdup(categoryPath->path); |
| 130 | categoryGroup = strtok(path, ARRAY_SEPERATOR); |
| 131 | if (audioTypeValidCategoryGroupName(categoryPath->feature->audioType, categoryGroup) == APP_ERROR) |
| 132 | { |
| 133 | free(path); |
| 134 | return APP_ERROR; |
| 135 | } |
| 136 | |
| 137 | while ((categoryGroup = strtok(NULL, ARRAY_SEPERATOR)) != NULL) |
| 138 | { |
| 139 | if (audioTypeValidCategoryGroupName(categoryPath->feature->audioType, categoryGroup) == APP_ERROR) |
| 140 | { |
| 141 | free(path); |
| 142 | return APP_ERROR; |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | free(path); |
| 147 | return APP_NO_ERROR; |
| 148 | } |
| 149 | |
| 150 | EXPORT CategoryPath *categoryPathCreate(Feature *feature, const char *path) |
| 151 | { |
| 152 | CategoryPath *categoryPath = malloc(sizeof(CategoryPath)); |
| 153 | categoryPath->path = strdup(path); |
| 154 | categoryPath->feature = feature; |
| 155 | |
| 156 | #ifdef WIN32 |
| 157 | /* The category path validation only run on win32 */ |
| 158 | if (categoryPathValidation(categoryPath) == APP_ERROR) |
| 159 | { |
| 160 | ERR_LOG("The %s feature's category path is not belong to categoryGroup! (%s)\n", feature->name, categoryPath->path); |
| 161 | categoryPathRelease(categoryPath); |
| 162 | return NULL; |
| 163 | } |
| 164 | #endif |
| 165 | |
| 166 | return categoryPath; |
| 167 | } |
| 168 | |
| 169 | EXPORT void categoryPathRelease(CategoryPath *categoryPath) |
| 170 | { |
| 171 | if (categoryPath) |
| 172 | { |
| 173 | free(categoryPath->path); |
| 174 | free(categoryPath); |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | EXPORT FeatureField *featureFieldCreate(FieldInfo *fieldInfo) |
| 179 | { |
| 180 | FeatureField *featureField = malloc(sizeof(FeatureField)); |
| 181 | featureField->fieldInfo = fieldInfo; |
| 182 | |
| 183 | return featureField; |
| 184 | } |
| 185 | |
| 186 | EXPORT void featureFieldRelease(FeatureField *featureField) |
| 187 | { |
| 188 | if (featureField) |
| 189 | { |
| 190 | free(featureField); |
| 191 | } |
| 192 | } |
| 193 | |
| 194 | EXPORT Feature *treeRootGetFeatureByName(TreeRoot *treeRoot, const char *featureName) |
| 195 | { |
| 196 | Feature *feature; |
| 197 | HASH_FIND_STR(treeRoot->featureHash, featureName, feature); |
| 198 | return feature; |
| 199 | } |
| 200 | |
| 201 | CategoryPath *findFeatureCategoryPath(char **arr, int *Switch, int n, Feature *feature) |
| 202 | { |
| 203 | CategoryPath *categoryPath = NULL; |
| 204 | UT_string *path = NULL; |
| 205 | int i; |
| 206 | |
| 207 | /* Generate the search string */ |
| 208 | utstring_new(path); |
| 209 | for (i = 0; i < n; ++i) |
| 210 | { |
| 211 | if (i == n - 1) |
| 212 | { |
| 213 | utstring_printf(path, "%s", arr[Switch[i]]); |
| 214 | } |
| 215 | else |
| 216 | { |
| 217 | utstring_printf(path, "%s,", arr[Switch[i]]); |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | /* Find the categoryPath */ |
| 222 | HASH_FIND_STR(feature->categoryPathHash, utstring_body(path), categoryPath); |
| 223 | DEBUG_LOG("Search path = %s, paramTree = 0x%p\n", utstring_body(path), categoryPath); |
| 224 | |
| 225 | utstring_free(path); |
| 226 | return categoryPath; |
| 227 | } |
| 228 | |
| 229 | CategoryPath *fuzzySearchFeatureCategoryPath(char **arr, int totalSize, int pickSize, Feature *feature) |
| 230 | { |
| 231 | CategoryPath *categoryPath = NULL; |
| 232 | int i, j, pos = pickSize - 1; |
| 233 | int *swpArray; |
| 234 | |
| 235 | if (pickSize > totalSize) |
| 236 | { |
| 237 | return categoryPath; |
| 238 | } |
| 239 | |
| 240 | swpArray = (int *)malloc(sizeof(int) * totalSize); |
| 241 | |
| 242 | for (i = 0; i < totalSize; ++i) |
| 243 | { |
| 244 | swpArray[i] = i; |
| 245 | } |
| 246 | |
| 247 | categoryPath = findFeatureCategoryPath(arr, swpArray, pickSize, feature); |
| 248 | if (categoryPath) |
| 249 | { |
| 250 | free(swpArray); |
| 251 | return categoryPath; |
| 252 | } |
| 253 | |
| 254 | do |
| 255 | { |
| 256 | if (swpArray[pickSize - 1] == totalSize - 1) |
| 257 | { |
| 258 | --pos; |
| 259 | } |
| 260 | else |
| 261 | { |
| 262 | pos = pickSize - 1; |
| 263 | } |
| 264 | |
| 265 | ++swpArray[pos]; |
| 266 | |
| 267 | for (j = pos + 1; j < pickSize; ++j) |
| 268 | { |
| 269 | swpArray[j] = swpArray[j - 1] + 1; |
| 270 | } |
| 271 | |
| 272 | categoryPath = findFeatureCategoryPath(arr, swpArray, pickSize, feature); |
| 273 | if (categoryPath) |
| 274 | { |
| 275 | free(swpArray); |
| 276 | return categoryPath; |
| 277 | } |
| 278 | |
| 279 | } |
| 280 | while (swpArray[0] < totalSize - pickSize); |
| 281 | |
| 282 | free(swpArray); |
| 283 | return categoryPath; |
| 284 | } |
| 285 | |
| 286 | CategoryPath *searchFeatureCategoryPath(Feature *feature, const char *categoryPath) |
| 287 | { |
| 288 | CategoryPath *featureCategoryPath; |
| 289 | char **categoryArray; |
| 290 | char *category; |
| 291 | char *tmpStr; |
| 292 | size_t numOfCategoryType; |
| 293 | size_t numOfCategory; |
| 294 | size_t i = 0; |
| 295 | |
| 296 | DEBUG_LOG("+Feature = %s, categoryPath = %s\n", feature->name, categoryPath); |
| 297 | |
| 298 | /* Full path search first */ |
| 299 | HASH_FIND_STR(feature->categoryPathHash, categoryPath, featureCategoryPath); |
| 300 | if (featureCategoryPath) |
| 301 | { |
| 302 | DEBUG_LOG("fuzzySearch paramTree found. (path = %s)\n", featureCategoryPath->path); |
| 303 | return featureCategoryPath; |
| 304 | } |
| 305 | else if (!strcmp(categoryPath, "")) |
| 306 | { |
| 307 | return NULL; |
| 308 | } |
| 309 | |
| 310 | /* Setup array for fuzzy search path enum */ |
| 311 | numOfCategoryType = audioTypeGetNumOfCategoryType(feature->audioType); |
| 312 | categoryArray = malloc(sizeof(char *) * numOfCategoryType); |
| 313 | |
| 314 | tmpStr = strdup(categoryPath ? categoryPath : ""); |
| 315 | category = strtok(tmpStr, ARRAY_SEPERATOR); |
| 316 | if (!category) |
| 317 | { |
| 318 | ERR_LOG("Cannot parse category\n"); |
| 319 | free(categoryArray); |
| 320 | free(tmpStr); |
| 321 | return NULL; |
| 322 | } |
| 323 | categoryArray[i++] = category; |
| 324 | |
| 325 | while ((category = strtok(NULL, ARRAY_SEPERATOR)) != NULL) |
| 326 | { |
| 327 | categoryArray[i++] = category; |
| 328 | } |
| 329 | numOfCategory = i; |
| 330 | |
| 331 | /* Fuzzy search */ |
| 332 | for (i = 1; i < numOfCategory; i++) |
| 333 | { |
| 334 | featureCategoryPath = fuzzySearchFeatureCategoryPath(categoryArray, numOfCategory, numOfCategory - i, feature); |
| 335 | if (featureCategoryPath) |
| 336 | { |
| 337 | break; |
| 338 | } |
| 339 | } |
| 340 | |
| 341 | if (!featureCategoryPath) |
| 342 | { |
| 343 | /* If no paramTree found, try to get the root paramTree */ |
| 344 | HASH_FIND_STR(feature->categoryPathHash, "", featureCategoryPath); |
| 345 | } |
| 346 | |
| 347 | free(categoryArray); |
| 348 | free(tmpStr); |
| 349 | |
| 350 | DEBUG_LOG("-fuzzySearch featureCategoryPath %s found. \n", featureCategoryPath ? "" : "not "); |
| 351 | return featureCategoryPath; |
| 352 | } |
| 353 | |
| 354 | EXPORT int featureIsCategoryPathSupport(Feature *feature, const char *categoryPath) |
| 355 | { |
| 356 | /* Get the category path */ |
| 357 | CategoryPath *featureCategoryPath = NULL; |
| 358 | UT_string *searchPath; |
| 359 | |
| 360 | if (!feature) |
| 361 | { |
| 362 | ERR_LOG("feature is NULL\n"); |
| 363 | return 0; |
| 364 | } |
| 365 | |
| 366 | if (!categoryPath) |
| 367 | { |
| 368 | ERR_LOG("categoryPath is NULL\n"); |
| 369 | return 0; |
| 370 | } |
| 371 | |
| 372 | /* Check if feature support all categoryPath first */ |
| 373 | featureCategoryPath = searchFeatureCategoryPath(feature, ""); |
| 374 | if (featureCategoryPath) |
| 375 | { |
| 376 | return 1; |
| 377 | } |
| 378 | |
| 379 | searchPath = utilNormalizeCategoryGroupPathForAudioType(categoryPath, feature->audioType); |
| 380 | if (!searchPath) |
| 381 | { |
| 382 | ERR_LOG("Cannot normalize categoryPath for %s AudioType. (path = %s)\n", feature->audioType->name, categoryPath); |
| 383 | return 0; |
| 384 | } |
| 385 | |
| 386 | /* Search the feature's category path */ |
| 387 | featureCategoryPath = searchFeatureCategoryPath(feature, utstring_body(searchPath)); |
| 388 | utstring_free(searchPath); |
| 389 | |
| 390 | if (featureCategoryPath) |
| 391 | { |
| 392 | return 1; |
| 393 | } |
| 394 | else |
| 395 | { |
| 396 | return 0; |
| 397 | } |
| 398 | } |