ec1246be22fd25e6ac302157b37a47911fdc1a41
kent
  Mon Dec 9 10:51:58 2013 -0800
Adding a bunch of functions to help work with output of jsParse.
diff --git src/hg/lib/jsHelper.c src/hg/lib/jsHelper.c
index 6da5d6e..1271019 100644
--- src/hg/lib/jsHelper.c
+++ src/hg/lib/jsHelper.c
@@ -557,31 +557,31 @@
 
 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(8));
+        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)
@@ -800,31 +800,31 @@
     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(0);
+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;
@@ -1056,15 +1056,283 @@
 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;
+}
+