4898794edd81be5285ea6e544acbedeaeb31bf78 max Tue Nov 23 08:10:57 2021 -0800 Fixing pointers to README file for license in all source code files. refs #27614 diff --git src/lib/jsonQuery.c src/lib/jsonQuery.c index fdb0cf3..deb8116 100644 --- src/lib/jsonQuery.c +++ src/lib/jsonQuery.c @@ -1,407 +1,407 @@ /* jsonQuery - simple path syntax for retrieving specific descendants of a jsonElement. */ /* Copyright (C) 2018 The Regents of the University of California - * See README in this or parent directory for licensing information. */ + * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */ #include "common.h" #include "dystring.h" #include "hash.h" #include "jsonQuery.h" static const char *findEndBracket(const char *path) /* Return a pointer to the right bracket matching the first left bracket that we encounter. */ { if (path == NULL) return NULL; const char *end = NULL; int leftCount = 0, rightCount = 0; int i; for (i = 0; path[i] != '\0'; i++) { if (path[i] == '[') leftCount++; else if (path[i] == ']') { rightCount++; if (rightCount == leftCount) { end = path+i; break; } else if (rightCount > leftCount) errAbort("findEndBracket: encountered right bracket before left bracket in '%s'", path); } } return end; } static char *jsonPathPopHead(const char *pathIn, char **retPath, struct lm *lm) /* Return the first component of pathIn and set retPath to the start of the next component in pathIn. * If pathIn is empty/NULL, set retPath to NULL and return the empty string. */ { if (isEmpty(pathIn)) { *retPath = NULL; return lm ? "" : cloneString(""); } else { char *pDot = strchr(pathIn, '.'); char *pBracket = strchr(pathIn, '['); if (pDot && pBracket) { // Both found -- ignore the second one, handle only the first one. if (pDot < pBracket) pBracket = NULL; else pDot = NULL; } if (pDot) { if (pDot == pathIn) errAbort("jsonPathPopHead: path '%s' should not start with '.'", pathIn); *retPath = pDot + 1; return lm ? lmCloneStringZ(lm, pathIn, pDot - pathIn) : cloneStringZ(pathIn, pDot - pathIn); } else if (pBracket) { if (pBracket == pathIn) { // This path component is a list index; return contents of [] const char *pEnd = findEndBracket(pathIn); if (!pEnd) errAbort("jsonPathPopHead: no matching ']' for '[' in '%s'", pathIn); if (pBracket[1] != '*' && !isdigit(pBracket[1]) && !strchr(pBracket, '=')) errAbort("jsonPathPopHead: contents of '[]' must be '*', a number, " "or a path=val condition (not %s)", pathIn); *retPath = (char *)(pEnd + 1); if (*retPath[0] == '.') *retPath = *retPath + 1; return lm ? lmCloneStringZ(lm, pathIn + 1, pEnd - pBracket - 1) : cloneStringZ(pathIn + 1, pEnd - pBracket - 1); } else { // The next path component is a list index *retPath = pBracket; return lm ? lmCloneStringZ(lm, pathIn, pBracket - pathIn) : cloneStringZ(pathIn, pBracket - pathIn); } } else { // Last component in path *retPath = (char *)pathIn + strlen(pathIn); return lm ? lmCloneString(lm, pathIn) : cloneString(pathIn); } } errAbort("jsonPathPopHead: should have returned a result before this point"); return NULL; } // Forward declaration for mutual recursion: static void rQueryElement(struct jsonElement *elIn, char *name, char *path, struct slRef **pResultList, struct lm *lm); /* Recursively search for descendants of jsonElements in inList matching path; add jsonElements * that match to resultList. */ static void rQueryObject(struct jsonElement *el, char *name, char *id, char *path, struct slRef **pResultList, struct lm *lm) /* Given a JSON object and a child id, recursively search child for path. */ { struct hash *hash = jsonObjectVal(el, name); if (hash) { struct jsonElement *child = hashFindVal(hash, id); if (child) rQueryElement(child, name, path, pResultList, lm); } } static void rQueryList(struct jsonElement *el, char *name, char *id, char *path, struct slRef **pResultList, struct lm *lm) /* Given a JSON list and a child index (* for all children), recursively search child(ren) if found * for path. */ { struct slRef *list = jsonListVal(el, name); char *equals = strchr(id, '='); if (equals) { // Conditional query; filter list items by condPath=val. char *value = equals+1; long intVal = atol(value); double doubleVal = atof(value); boolean booleanVal = FALSE, valIsBoolean = FALSE; if (sameString(value, "true") || sameString(value, "TRUE") || sameString(value, "1")) { booleanVal = TRUE; valIsBoolean = TRUE; } else if (sameString(value, "false") || sameString(value, "FALSE") || sameString(value, "0")) { booleanVal = FALSE; valIsBoolean = TRUE; } char condPath[strlen(id)+1]; safencpy(condPath, sizeof condPath, id, (equals - id)); struct slRef *ref; for (ref = list; ref != NULL; ref = ref->next) { struct slRef *condPathResults = NULL; rQueryElement(ref->val, name, condPath, &condPathResults, lm); struct slRef *resRef; for (resRef = condPathResults; resRef != NULL; resRef = resRef->next) { struct jsonElement *resEl = resRef->val; boolean matches = FALSE; switch (resEl->type) { case jsonString: matches = sameString(jsonStringVal(resEl, condPath), value); break; case jsonNumber: matches = (jsonNumberVal(resEl, condPath) == intVal); break; case jsonDouble: matches = (jsonDoubleVal(resEl, condPath) == doubleVal); break; case jsonBoolean: if (!valIsBoolean) errAbort("jsonQueryElement: bad conditional value '%s' for boolean", value); matches = (jsonBooleanVal(resEl, condPath) == booleanVal); break; case jsonNull: matches = (sameString(value, "NULL") || sameString(value, "null")); break; default: errAbort("jsonQueryElement: bad jsonElementType %d for conditional query", resEl->type); } if (matches) { rQueryElement(ref->val, name, path, pResultList, lm); break; } } } } else { int idIx = -1; if (isdigit(id[0])) idIx = atoi(id); else if (differentString(id, "*")) errAbort("jsonQueryElement: invalid index '%s' for list", id); struct slRef *ref; int ix; for (ref = list, ix = 0; ref != NULL; ref = ref->next, ix++) { if (idIx < 0 || ix == idIx) { rQueryElement(ref->val, name, path, pResultList, lm); if (ix == idIx) break; } } } } static void rQueryElement(struct jsonElement *elIn, char *name, char *path, struct slRef **pResultList, struct lm *lm) /* Recursively search for descendants of jsonElements in inList matching path; add jsonElements * that match to resultList. */ { char *pathNext = NULL; char *id = jsonPathPopHead(path, &pathNext, lm); struct dyString *dy = dyStringCreate("%s", name); if (isNotEmpty(id)) { switch (elIn->type) { case jsonObject: { dyStringPrintf(dy, ".%s", id); rQueryObject(elIn, dy->string, id, pathNext, pResultList, lm); break; } case jsonList: { dyStringPrintf(dy, "[%s]", id); rQueryList(elIn, dy->string, id, pathNext, pResultList, lm); break; } case jsonString: case jsonBoolean: case jsonNumber: case jsonDouble: case jsonNull: { errAbort("jsonQueryElement: got element with scalar type (%d), but children specified " "(%s)", elIn->type, id); break; } default: { errAbort("jsonQueryElement: invalid type: %d", elIn->type); break; } } } else { struct slRef *ref; if (lm) lmAllocVar(lm, ref) else AllocVar(ref); ref->val = elIn; slAddHead(pResultList, ref); } if (lm == NULL) freez(&id); dyStringFree(&dy); } struct slRef *jsonQueryElementList(struct slRef *inList, char *name, char *path, struct lm *lm) /* Return a ref list of jsonElement descendants matching path of all jsonElements in inList. * name is for error reporting. */ { struct slRef *resultList = NULL; struct dyString *dy = dyStringNew(0); boolean isMult = (inList->next != NULL); struct slRef *ref; int ix; for (ref = inList, ix = 0; ref != NULL; ref = ref->next, ix++) { struct jsonElement *elIn = ref->val; if (elIn) { dyStringClear(dy); dyStringPrintf(dy, "%s", name); if (isMult) dyStringPrintf(dy, "[%d]", ix); rQueryElement(elIn, dy->string, path, &resultList, lm); } } slReverse(&resultList); dyStringFree(&dy); return resultList; } struct slRef *jsonQueryElement(struct jsonElement *el, char *name, char *path, struct lm *lm) /* Return a ref list of jsonElement descendants of el that match path. * name is for error reporting. */ { // Make an slRef wrapper for el and call jsonQueryElementList. struct slRef elRef = { NULL, el }; return jsonQueryElementList(&elRef, name, path, lm); } static struct jsonElement *querySingle(struct jsonElement *el, char *name, char *path, struct lm *lm) /* Return one jsonElement resulting from searching el for path (or NULL); errAbort if multiple. */ { struct jsonElement *resEl = NULL; struct slRef ref = { NULL, el }; struct slRef *resultRef = jsonQueryElementList(&ref, name, path, lm); if (resultRef) { if (resultRef->next) errAbort("jsonQuerySingle: expected single result but got %d results", slCount(resultRef)); resEl = resultRef->val; } if (lm == NULL) freeMem(resultRef); return resEl; } char *jsonQueryString(struct jsonElement *el, char *name, char *path, struct lm *lm) /* Alloc & return the string value at the end of path in el. May be NULL. */ { struct jsonElement *resEl = querySingle(el, name, path, lm); if (resEl == NULL) return NULL; else if (lm) return lmCloneString(lm, jsonStringVal(resEl, path)); else return cloneString(jsonStringVal(resEl, path)); } long jsonQueryInt(struct jsonElement *el, char *name, char *path, long defaultVal, struct lm *lm) /* Return the int value at path in el, or defaultVal if not found. */ { struct jsonElement *resEl = querySingle(el, name, path, lm); long result = resEl ? jsonNumberVal(resEl, path) : defaultVal; if (lm == NULL) freeMem(resEl); return result; } boolean jsonQueryBoolean(struct jsonElement *el, char *name, char *path, boolean defaultVal, struct lm *lm) /* Return the boolean value at path in el, or defaultVal if not found. */ { struct jsonElement *resEl = querySingle(el, name, path, lm); boolean result = resEl ? jsonBooleanVal(resEl, path) : defaultVal; if (lm == NULL) freeMem(resEl); return result; } struct slName *jsonQueryStringList(struct slRef *inList, char *name, char *path, struct lm *lm) /* Alloc & return a list of string values matching path in all elements of inList. May be NULL. */ { struct slName *results = NULL; struct slRef *resultRefs = jsonQueryElementList(inList, name, path, lm); struct slRef *ref; for (ref = resultRefs; ref != NULL; ref = ref->next) { struct jsonElement *resEl = ref->val; char *string = jsonStringVal(resEl, path); struct slName *sln = lm ? lmSlName(lm, string) : slNameNew(string); slAddHead(&results, sln); } slReverse(&results); if (lm == NULL && resultRefs) slFreeList(resultRefs); return results; } struct slInt *jsonQueryIntList(struct slRef *inList, char *name, char *path, struct lm *lm) /* Alloc & return a list of int values matching path in all elements of inList. May be NULL. */ { struct slInt *results = NULL; struct slRef *resultRefs = jsonQueryElementList(inList, name, path, lm); struct slRef *ref; for (ref = resultRefs; ref != NULL; ref = ref->next) { struct jsonElement *resEl = ref->val; int val = jsonNumberVal(resEl, path); struct slInt *sli; if (lm) lmAllocVar(lm, sli) else AllocVar(sli); sli->val = val; slAddHead(&results, sli); } slReverse(&results); if (lm == NULL && resultRefs) slFreeList(resultRefs); return results; } struct slName *jsonQueryStrings(struct jsonElement *el, char *name, char *path, struct lm *lm) /* Alloc & return a list of string values matching path in el. May be NULL. */ { struct slRef elRef = { NULL, el }; return jsonQueryStringList(&elRef, name, path, lm); } struct slInt *jsonQueryInts(struct jsonElement *el, char *name, char *path, struct lm *lm) /* Alloc & return a list of int values matching path in el. May be NULL. */ { struct slRef elRef = { NULL, el }; return jsonQueryIntList(&elRef, name, path, lm); }