93943b9bd3f5d6d9ef5a51e636636449b0c607b1
kent
  Mon Dec 9 11:10:26 2013 -0800
Separating out generic portable JSON parser from Genome Browser specific stuff in jsHelper module, creating jsonParse module.
diff --git src/hg/lib/jsHelper.c src/hg/lib/jsHelper.c
index 1271019..cade588 100644
--- src/hg/lib/jsHelper.c
+++ src/hg/lib/jsHelper.c
@@ -3,32 +3,30 @@
 #include "common.h"
 #include <regex.h>
 #include "dystring.h"
 #include "cheapcgi.h"
 #include "cart.h"
 #include "hPrint.h"
 #include "hash.h"
 #include "jsHelper.h"
 #include "web.h"
 #include "hui.h"
 #include "hgConfig.h"
 #include "portable.h"
 
 static boolean jsInited = FALSE;
 
-struct jsonElement *jsonGlobalsHash = NULL;
-
 /* mainForm/hiddenForm code supports the following: when the user selects
  * something from a pull-down, it will go hit the server to
  * figure out how to reload other control options based on the choice.
  * (For instance if they change the group, which items in the track
  * drop-down need to change).
  *
  * We accomplish this by maintaining two forms - a mainForm and a
  * hiddenForm.  The hiddenForm maintains echo's of all the variables
  * in the main form, which get updated onChange of controls that need
  * to 'ripple' to other controls.  The onChange also submits the
  * control. */
 
 void jsInit()
 /* If this is the first call, set window.onload to the operations
  * performed upon loading a page and print supporting javascript.
@@ -499,103 +497,30 @@
        "window.onload = function() { "
        "  if (checkPageBackOrRefresh()) { \n"
        "    if (window.location.search == '?%s') { \n"
 	      // We already have the hgsid-only URL that we want, reload it.
 	      // (necessary for IE because IE doesn't reload on replace,
 	      //  unless window.location and/or window.search changes)
        "      window.location.reload(true);\n"
        "    } else { \n"
        "      window.location.replace('%s?%s');\n"
        "    } \n"
        "  } "
        "};\n"
        "</script>\n", cartSidUrlString(cart), cgiScriptName(), cartSidUrlString(cart));
 }
 
-static struct jsonElement *newJsonElement(jsonElementType type)
-// generic constructor for a jsonElement; callers fill in the appropriate value
-{
-struct jsonElement *ele;
-AllocVar(ele);
-ele->type = type;
-return ele;
-}
-
-struct jsonElement *newJsonString(char *str)
-{
-struct jsonElement *ele = newJsonElement(jsonString);
-ele->val.jeString = cloneString(str);
-return ele;
-}
-
-struct jsonElement *newJsonBoolean(boolean val)
-{
-struct jsonElement *ele = newJsonElement(jsonBoolean);
-ele->val.jeBoolean = val;
-return ele;
-}
-
-struct jsonElement *newJsonNumber(long val)
-{
-struct jsonElement *ele = newJsonElement(jsonNumber);
-ele->val.jeNumber = val;
-return ele;
-}
-
-struct jsonElement *newJsonDouble(double val)
-{
-struct jsonElement *ele = newJsonElement(jsonDouble);
-ele->val.jeDouble = val;
-return ele;
-}
-
-struct jsonElement *newJsonObject(struct hash *h)
-{
-struct jsonElement *ele = newJsonElement(jsonObject);
-ele->val.jeHash = h;
-return ele;
-}
-
-struct jsonElement *newJsonList(struct slRef *list)
-{
-struct jsonElement *ele = newJsonElement(jsonList);
-ele->val.jeList = list;
-return ele;
-}
-
-void jsonObjectAdd(struct jsonElement *h, char *name, struct jsonElement *ele)
-// Add a new element to a jsonObject; existing values are replaced.
-// NOTE: Adding to a NULL hash will add to the global "common" hash printed with jsonPrintGlobals();
-{
-if (h == NULL)  // If hash isn't provided, assume global
-    {
-    if (jsonGlobalsHash == NULL)
-        jsonGlobalsHash = newJsonObject(newHash(5));
-    h = jsonGlobalsHash;
-    }
-if(h->type != jsonObject)
-    errAbort("jsonObjectAdd called on element with incorrect type (%d)", h->type);
-hashReplace(h->val.jeHash, name, ele);
-}
-
-void jsonListAdd(struct jsonElement *list, struct jsonElement *ele)
-{
-if(list->type != jsonList)
-    errAbort("jsonListAdd called on element with incorrect type (%d)", list->type);
-slAddHead(&list->val.jeList, ele);
-}
-
 static char *makeIndentBuf(int indentLevel)
 {
 if (indentLevel < 0)
     return "";
 char *indentBuf;
 indentBuf = needMem(indentLevel + 1);
 memset(indentBuf, '\t', indentLevel);
 indentBuf[indentLevel] = 0;
 return indentBuf;
 }
 
 static void jsonPrintRecurse(struct jsonElement *ele, int indentLevel)
 {
 if (indentLevel >= -1) // Note that < -1 will result in no indenting
     indentLevel++;
@@ -717,622 +642,15 @@
 
 void jsonErrPrintf(struct dyString *ds, char *format, ...)
 //  Printf a json error to a dyString for communicating with ajax code; format is:
 //  {"error": error message here}
 {
 va_list args;
 va_start(args, format);
 dyStringPrintf(ds, "{\"error\": \"");
 struct dyString *buf = newDyString(1000);
 dyStringVaPrintf(buf, format, args);
 dyStringAppend(ds, jsonStringEscape(dyStringCannibalize(&buf)));
 dyStringPrintf(ds, "\"}");
 va_end(args);
 }
 
-static void skipLeadingSpacesWithPos(char *s, int *posPtr)
-/* skip leading white space. */
-{
-for (;;)
-    {
-    char c = s[*posPtr];
-    if (!isspace(c))
-	return;
-    (*posPtr)++;
-    }
-}
-
-static void getSpecificChar(char c, char *str, int *posPtr)
-{
-// get specified char from string or errAbort
-if(str[*posPtr] != c)
-    errAbort("Unexpected character '%c' (expected '%c') - string position %d\n", str[*posPtr], c, *posPtr);
-(*posPtr)++;
-}
-
-static char *getString(char *str, int *posPtr)
-{
-// read a double-quote delimited string; we handle backslash escaping.
-// returns allocated string.
-boolean escapeMode = FALSE;
-int i;
-struct dyString *ds = dyStringNew(1024);
-getSpecificChar('"', str, posPtr);
-for(i = 0;; i++)
-    {
-    char c = str[*posPtr + i];
-    if(!c)
-        errAbort("Premature end of string (missing trailing double-quote); string position '%d'", *posPtr);
-    else if(escapeMode)
-        {
-        // We support escape sequences listed in http://www.json.org,
-        // except for Unicode which we cannot support in C-strings
-        switch(c)
-            {
-            case 'b':
-                c = '\b';
-                break;
-            case 'f':
-                c = '\f';
-                break;
-            case 'n':
-                c = '\n';
-                break;
-            case 'r':
-                c = '\r';
-                break;
-            case 't':
-                c = '\t';
-                break;
-            case 'u':
-                errAbort("Unicode in JSON is unsupported");
-                break;
-            default:
-                // we don't need to convert \,/ or "
-                break;
-            }
-        dyStringAppendC(ds, c);
-        escapeMode = FALSE;
-        }
-    else if(c == '"')
-        break;
-    else if(c == '\\')
-        escapeMode = TRUE;
-    else
-        {
-        dyStringAppendC(ds, c);
-        escapeMode = FALSE;
-        }
-    }
-*posPtr += i;
-getSpecificChar('"', str, posPtr);
-return dyStringCannibalize(&ds);
-}
-
-static struct jsonElement *jsonParseExpression(char *str, int *posPtr);
-
-static struct jsonElement *jsonParseObject(char *str, int *posPtr)
-{
-struct hash *h = newHash(5);
-getSpecificChar('{', str, posPtr);
-while(str[*posPtr] != '}')
-    {
-    // parse out a name : val pair
-    skipLeadingSpacesWithPos(str, posPtr);
-    char *name = getString(str, posPtr);
-    skipLeadingSpacesWithPos(str, posPtr);
-    getSpecificChar(':', str, posPtr);
-    skipLeadingSpacesWithPos(str, posPtr);
-    hashAdd(h, name, jsonParseExpression(str, posPtr));
-    skipLeadingSpacesWithPos(str, posPtr);
-    if(str[*posPtr] == ',')
-        (*posPtr)++;
-    else
-        break;
-    }
-skipLeadingSpacesWithPos(str, posPtr);
-getSpecificChar('}', str, posPtr);
-return newJsonObject(h);
-}
-
-static struct jsonElement *jsonParseList(char *str, int *posPtr)
-{
-struct slRef *list = NULL;
-getSpecificChar('[', str, posPtr);
-while(str[*posPtr] != ']')
-    {
-    struct slRef *e;
-    AllocVar(e);
-    skipLeadingSpacesWithPos(str, posPtr);
-    e->val = jsonParseExpression(str, posPtr);
-    slAddHead(&list, e);
-    skipLeadingSpacesWithPos(str, posPtr);
-    if(str[*posPtr] == ',')
-        (*posPtr)++;
-    else
-        break;
-    }
-skipLeadingSpacesWithPos(str, posPtr);
-getSpecificChar(']', str, posPtr);
-slReverse(&list);
-return newJsonList(list);
-}
-
-static struct jsonElement *jsonParseString(char *str, int *posPtr)
-{
-return newJsonString(getString(str, posPtr));
-}
-
-static struct jsonElement *jsonParseBoolean(char *str, int *posPtr)
-{
-struct jsonElement *ele = NULL;
-int i;
-for(i = 0; str[*posPtr + i] && isalpha(str[*posPtr + i]); i++)
-    ;
-char *val = cloneStringZ(str + *posPtr, i);
-if(sameString(val, "true"))
-    ele = newJsonBoolean(TRUE);
-else if(sameString(val, "false"))
-    ele =  newJsonBoolean(FALSE);
-else
-    errAbort("Invalid boolean value '%s'; pos: %d", val, *posPtr);
-*posPtr += i;
-freez(&val);
-return ele;
-}
-
-static struct jsonElement *jsonParseNumber(char *str, int *posPtr)
-{
-int i;
-boolean integral = TRUE;
-struct jsonElement *retVal = NULL;
-
-for(i = 0;; i++)
-    {
-    char c = str[*posPtr + i];
-    if(c == 'e' || c == 'E' || c == '.')
-        integral = FALSE;
-    else if(!c || (!isdigit(c) && c != '-'))
-        break;
-    }
-char *val = cloneStringZ(str + *posPtr, i);
-*posPtr += i;
-if(integral)
-    retVal = newJsonNumber(sqlLongLong(val));
-else
-    {
-    double d;
-    if(sscanf(val, "%lf", &d))
-        retVal = newJsonDouble(d);
-    else
-        errAbort("Invalid JSON Double: %s", val);
-    }
-freez(&val);
-return retVal;
-}
-
-static struct jsonElement *jsonParseExpression(char *str, int *posPtr)
-{
-skipLeadingSpacesWithPos(str, posPtr);
-char c = str[*posPtr];
-if(c == '{')
-    return jsonParseObject(str, posPtr);
-else if (c == '[')
-    return jsonParseList(str, posPtr);
-else if (c == '"')
-    return jsonParseString(str, posPtr);
-else if (isdigit(c) || c == '-')
-    return jsonParseNumber(str, posPtr);
-else
-    return jsonParseBoolean(str, posPtr);
-// XXXX support null?
-}
-
-struct jsonElement *jsonParse(char *str)
-{
-// parse string into an in-memory json representation
-int pos = 0;
-struct jsonElement *ele = jsonParseExpression(str, &pos);
-skipLeadingSpacesWithPos(str, &pos);
-if(str[pos])
-    errAbort("Invalid JSON: unprocessed trailing string at position: %d: %s", pos, str + pos);
-return ele;
-}
-
-char *jsonStringEscape(char *inString)
-/* backslash escape a string for use in a double quoted json string.
- * More conservative than javaScriptLiteralEncode because
- * some json parsers complain if you escape & or ' */
-{
-char c;
-int outSize = 0;
-char *outString, *out, *in;
-
-if (inString == NULL)
-    return(cloneString(""));
-
-/* Count up how long it will be */
-in = inString;
-while ((c = *in++) != 0)
-    {
-    switch(c)
-        {
-        case '\"':
-        case '\\':
-        case '/':
-        case '\b':
-        case '\f':
-        case '\n':
-        case '\r':
-        case '\t':
-            outSize += 2;
-            break;
-        default:
-            outSize += 1;
-        }
-    }
-outString = needMem(outSize+1);
-
-/* Encode string */
-in = inString;
-out = outString;
-while ((c = *in++) != 0)
-    {
-    switch(c)
-        {
-        case '\"':
-        case '\\':
-        case '/':
-        case '\b':
-        case '\f':
-        case '\n':
-        case '\r':
-        case '\t':
-            *out++ = '\\';
-            break;
-        }
-    *out++ = c;
-    }
-*out++ = 0;
-return outString;
-}
-
-void jsonFindNameRecurse(struct jsonElement *ele, char *jName, struct slName **pList)
-// Search the JSON tree recursively to find all the values associated to
-// the name, and add them to head of the list.  
-{
-switch (ele->type)
-    {
-    case jsonObject:
-        {
-        if(hashNumEntries(ele->val.jeHash))
-            {
-            struct hashEl *el, *list = hashElListHash(ele->val.jeHash);
-            slSort(&list, hashElCmp);
-            for (el = list; el != NULL; el = el->next)
-                {
-                struct jsonElement *val = el->val;
-                if sameString(el->name, jName)
-                    slNameAddHead(pList, jsonStringEscape(val->val.jeString));
-                jsonFindNameRecurse(val, jName, pList);
-                }
-            hashElFreeList(&list);
-            }
-        break;
-        }
-    case jsonList:
-        {
-        struct slRef *el;
-        if(ele->val.jeList)
-            {
-            for (el = ele->val.jeList; el != NULL; el = el->next)
-                {
-                struct jsonElement *val = el->val;
-                jsonFindNameRecurse(val, jName, pList);
-                }
-            }
-        break;
-        }
-    case jsonString:
-    case jsonBoolean:
-    case jsonNumber:
-    case jsonDouble:
-        {
-        break;
-        }
-    default:
-        {
-        errAbort("jsonFindNameRecurse; invalid type: %d", ele->type);
-        break;
-        }
-    }
-}
-
-struct slName *jsonFindName(struct jsonElement *json, char *jName)
-// Search the JSON tree to find all the values associated to the name
-// and add them to head of the list.  
-{
-struct slName *list = NULL;
-jsonFindNameRecurse(json, jName, &list);
-slReverse(&list);
-return list;
-}
-
-struct slName *jsonFindNameUniq(struct jsonElement *json, char *jName)
-// Search the JSON tree to find all the unique values associated to the name
-// and add them to head of the list. 
-{
-struct slName *list = NULL;
-jsonFindNameRecurse(json, jName, &list);
-slUniqify(&list, slNameCmp, slNameFree);
-slReverse(&list);
-return list;
-}
-
-void jsonElementRecurse(struct jsonElement *ele, char *name, boolean isLast,
-    void (*startCallback)(struct jsonElement *ele, char *name, boolean isLast, void *context),  
-    // Called at element start
-    void (*endCallback)(struct jsonElement *ele, char *name, boolean isLast, void *context),    
-    // Called at element end
-    void *context)
-/* Recurse through JSON tree calling callback functions with element and context.
- * Either startCallback or endCallback may be NULL*/
-{
-if (startCallback != NULL)
-    startCallback(ele, name, isLast, context);
-switch (ele->type)
-    {
-    case jsonObject:
-        {
-        if(hashNumEntries(ele->val.jeHash))
-            {
-            struct hashEl *el, *list = hashElListHash(ele->val.jeHash);
-            slSort(&list, hashElCmp);
-            for (el = list; el != NULL; el = el->next)
-                {
-                struct jsonElement *val = el->val;
-                jsonElementRecurse(val, el->name, el->next == NULL, 
-		    startCallback, endCallback, context);
-                }
-            hashElFreeList(&list);
-            }
-        break;
-        }
-    case jsonList:
-        {
-        struct slRef *el;
-        if(ele->val.jeList)
-            {
-            for (el = ele->val.jeList; el != NULL; el = el->next)
-                {
-                struct jsonElement *val = el->val;
-                jsonElementRecurse(val, NULL, el->next == NULL, 
-		    startCallback, endCallback, context);
-                }
-            }
-        break;
-        }
-    case jsonString:
-    case jsonBoolean:
-    case jsonNumber:
-    case jsonDouble:
-        {
-        break;
-        }
-    default:
-        {
-        errAbort("jsonElementRecurse; invalid type: %d", ele->type);
-        break;
-        }
-    }
-if (endCallback != NULL)
-    endCallback(ele, name, isLast, context);
-}
-
-void jsonPrintOneStart(struct jsonElement *ele, char *name, boolean isLast, int indent, FILE *f)
-/* Print the start of one json element - just name and maybe an opening brace or bracket.
- * Recursion is handled elsewhere. */
-{
-spaceOut(f, indent);
-if (name != NULL)
-    {
-    fprintf(f, "\"%s\": ", name);
-    }
-switch (ele->type)
-    {
-    case jsonObject:
-        {
-	fprintf(f, "{\n");
-        break;
-        }
-    case jsonList:
-        {
-	fprintf(f, "[\n");
-        break;
-        }
-    case jsonString:
-        {
-	char *escaped = jsonStringEscape(ele->val.jeString);
-	fprintf(f, "\"%s\"",  escaped);
-	freez(&escaped);
-	break;
-	}
-    case jsonBoolean:
-        {
-	char *val = (ele->val.jeBoolean ? "frue" : "false");
-	fprintf(f, "%s", val);
-	break;
-	}
-    case jsonNumber:
-        {
-	fprintf(f, "%ld", ele->val.jeNumber);
-	break;
-	}
-    case jsonDouble:
-        {
-	fprintf(f, "%g", ele->val.jeDouble);
-        break;
-        }
-    default:
-        {
-        errAbort("jsonPrintOneStart; invalid type: %d", ele->type);
-        break;
-        }
-    }
-}
-
-void jsonPrintOneEnd(struct jsonElement *ele, char *name, boolean isLast, boolean indent, FILE *f)
-/* Print object end */
-{
-switch (ele->type)
-    {
-    case jsonObject:
-        {
-	spaceOut(f, indent);
-	fprintf(f, "}");
-        break;
-        }
-    case jsonList:
-        {
-	spaceOut(f, indent);
-	fprintf(f, "]");
-        break;
-        }
-    case jsonString:
-    case jsonBoolean:
-    case jsonNumber:
-    case jsonDouble:
-        break;
-    default:
-        {
-        errAbort("jsonPrintOneEnd; invalid type: %d", ele->type);
-        break;
-        }
-    }
-if (!isLast)
-    fputc(',', f);
-fputc('\n', f);
-}
-
-struct jsonPrintContext
-/* Context for printing a JSON object nicely */
-    {
-    FILE *f;	// where to print it
-    int indent;	// How much to indent currently
-    int indentPer;  // How much to indent each level
-    };
-
-
-static void printIndentedNameStartCallback(struct jsonElement *ele, char *name, 
-    boolean isLast, void *context)
-{
-struct jsonPrintContext *jps = context;
-jsonPrintOneStart(ele, name,  isLast, jps->indent, jps->f);
-jps->indent += jps->indentPer;
-}
-
-static void printIndentedNameEndCallback(struct jsonElement *ele, char *name, 
-    boolean isLast, void *context)
-{
-struct jsonPrintContext *jps = context;
-jps->indent -= jps->indentPer;
-jsonPrintOneEnd(ele, name, isLast, jps->indent, stdout);
-}
-
-void jsonPrintToFile(struct jsonElement *root, char *name, FILE *f, int indentPer)
-/* Print out JSON object and all children nicely indented to f as JSON objects. 
- * Name may be NULL.  Implemented via jsonPrintOneStart/jsonPrintOneEnd. */
-{
-struct jsonPrintContext jps = {f, 0, indentPer};
-jsonElementRecurse(root, NULL, TRUE, 
-	printIndentedNameStartCallback, printIndentedNameEndCallback, &jps);
-}
-
-/** Routines that check json type and return corresponding value. **/
-
-struct slRef *jsonListVal(struct jsonElement *ele, char *name)
-/* Enforce element is type jsonList.  Return list value */
-{
-if (ele->type != jsonList)
-    errAbort("json element %s is not a list", name);
-return ele->val.jeList;
-}
-
-struct hash *jsonObjectVal(struct jsonElement *ele, char *name)
-/* Enforce object is type jsonObject.  Return object hash */
-{
-if (ele->type != jsonObject)
-    errAbort("json element %s is not an object", name);
-return ele->val.jeHash;
-}
-
-long jsonNumberVal(struct jsonElement *ele, char *name)
-/* Enforce element is type jsonNumber and return value. */
-{
-if (ele->type != jsonNumber)
-    errAbort("json element %s is not a number", name);
-return ele->val.jeNumber;
-}
-
-long jsonDoubleVal(struct jsonElement *ele, char *name)
-/* Enforce element is type jsonDouble and return value. */
-{
-if (ele->type != jsonDouble)
-    errAbort("json element %s is not a number", name);
-return ele->val.jeDouble;
-}
-
-long jsonBooleanVal(struct jsonElement *ele, char *name)
-/* Enforce element is type jsonBoolean and return value. */
-{
-if (ele->type != jsonBoolean)
-    errAbort("json element %s is not a boolean", name);
-return ele->val.jeBoolean;
-}
-
-char *jsonStringVal(struct jsonElement *ele, char *eleName)
-/* Enforce element is type jsonString and return value. */
-{
-if (ele->type != jsonString)
-    errAbort("json element %s is not a string", eleName);
-return ele->val.jeString;
-}
-
-/** Routines that help work with json objects (bracket enclosed key/val pairs **/
-
-struct jsonElement *jsonFindNamedField(struct jsonElement *object, 
-    char *objectName, char *field)
-/* Find named field of object or return NULL if not found.  Abort if object
- * is not actually an object. */
-{
-struct hash *hash = jsonObjectVal(object, objectName);
-return hashFindVal(hash, field);
-}
-
-struct jsonElement *jsonMustFindNamedField(struct jsonElement *object, 
-    char *objectName, char *field)
-/* Find named field of object or die trying. */
-{
-struct jsonElement *ele = jsonFindNamedField(object, objectName, field);
-if (ele == NULL)
-   errAbort("Couldn't find field %s in json object %s", field, objectName);
-return ele;
-}
-
-char *jsonOptionalStringField(struct jsonElement *object, char *field, char *defaultVal)
-/* Return string valued field of object, or defaultVal if it doesn't exist. */
-{
-struct jsonElement *ele = jsonFindNamedField(object, "", field);
-if (ele == NULL)
-     return defaultVal;
-return jsonStringVal(ele, field);
-}
-
-char *jsonStringField(struct jsonElement *object, char *field)
-/* Return string valued field of object or abort if field doesn't exist. */
-{
-char *val = jsonOptionalStringField(object, field, NULL);
-if (val == NULL)
-    errAbort("Field %s doesn't exist in json object", field);
-return val;
-}
-