]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/libopensrf/osrf_json_object.c
Finished adding doxygen-style comments to document the app session
[OpenSRF.git] / src / libopensrf / osrf_json_object.c
1 /*
2 Copyright (C) 2006  Georgia Public Library Service 
3 Bill Erickson <billserickson@gmail.com>
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 */
15
16 /**
17         @file osrf_json_object.c
18         @brief Implementation of the basic operations involving jsonObjects.
19
20         As a performance tweak: we maintain a free list of jsonObjects that have already
21         been allocated but are not currently in use.  When we need to create a jsonObject,
22         we can take one from the free list, if one is available, instead of calling
23         malloc().  Likewise when we free a jsonObject, we can stick it on the free list
24         for potential reuse instead of calling free().
25 */
26
27 #include <stdlib.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #include <limits.h>
31 #include <opensrf/log.h>
32 #include <opensrf/osrf_json.h>
33 #include <opensrf/osrf_json_utils.h>
34 #include <opensrf/osrf_utf8.h>
35
36 /* cleans up an object if it is morphing another object, also
37  * verifies that the appropriate storage container exists where appropriate */
38 /**
39         @brief Coerce a jsonObj into a specified type, if it isn't already of that type.
40         @param _obj_ Pointer to the jsonObject to be coerced.
41         @param newtype The desired type.
42
43         If the old type and the new type don't match, discard and free the old contents.
44
45         If the old type is JSON_STRING or JSON_NUMBER, free the internal string buffer even
46         if the type is not changing.
47
48         If the new type is JSON_ARRAY or JSON_HASH, make sure there is an osrfList or osrfHash
49         in the jsonObject, respectively.
50 */
51 #define JSON_INIT_CLEAR(_obj_, newtype)         \
52         if( _obj_->type == JSON_HASH && newtype != JSON_HASH ) {                        \
53                 osrfHashFree(_obj_->value.h);                   \
54                 _obj_->value.h = NULL;                                  \
55         } else if( _obj_->type == JSON_ARRAY && newtype != JSON_ARRAY ) {       \
56                 osrfListFree(_obj_->value.l);                   \
57                 _obj_->value.l = NULL;                                  \
58         } else if( _obj_->type == JSON_STRING || _obj_->type == JSON_NUMBER ) { \
59                 free(_obj_->value.s);                                   \
60                 _obj_->value.s = NULL;                                  \
61         } else if( _obj_->type == JSON_BOOL && newtype != JSON_BOOL ) { \
62                 _obj_->value.l = NULL;                                  \
63         } \
64         _obj_->type = newtype; \
65         if( newtype == JSON_HASH && _obj_->value.h == NULL ) {  \
66                 _obj_->value.h = osrfNewHash();         \
67                 osrfHashSetCallback( _obj_->value.h, _jsonFreeHashItem ); \
68         } else if( newtype == JSON_ARRAY && _obj_->value.l == NULL ) {  \
69                 _obj_->value.l = osrfNewList();         \
70                 _obj_->value.l->freeItem = _jsonFreeListItem;\
71         }
72
73 /** Count of the times we put a freed jsonObject on the free list instead of calling free() */
74 static int unusedObjCapture = 0;
75 /** Count of the times we reused a jsonObject from the free list instead of calling malloc() */
76 static int unusedObjRelease = 0;
77 /** Count of the times we allocated a jsonObject with malloc() */
78 static int mallocObjCreate = 0;
79 /** Number of unused jsonObjects currently on the free list */
80 static int currentListLen = 0;
81
82 /**
83         Union overlaying a jsonObject with a pointer.  When the jsonObject is not in use as a
84         jsonObject, we use the overlaid pointer to maintain a linked list of unused jsonObjects.
85 */
86 union unusedObjUnion{
87
88         union unusedObjUnion* next;
89         jsonObject obj;
90 };
91 typedef union unusedObjUnion unusedObj;
92
93 /** Pointer to the head of the free list */
94 static unusedObj* freeObjList = NULL;
95
96 static void add_json_to_buffer( const jsonObject* obj,
97         growing_buffer * buf, int do_classname, int second_pass );
98
99 /**
100         @brief Return all jsonObjects in the free list to the heap.
101
102         Reclaims memory occupied by unused jsonObjects in the free list.  It is never really
103         necessary to call this function, assuming that we don't run out of memory.  However
104         it might be worth calling if we have built and destroyed a lot of jsonObjects that
105         we don't expect to need again, in order to reduce our memory footprint.
106 */
107 void jsonObjectFreeUnused( void ) {
108
109         unusedObj* temp;
110         while( freeObjList ) {
111                 temp = freeObjList->next;
112                 free( freeObjList );
113                 freeObjList = temp;
114         }
115 }
116
117 /**
118         @brief Create a new jsonObject, optionally containing a string.
119         @param data Pointer to a string to be stored in the jsonObject; may be NULL.
120         @return Pointer to a newly allocate jsonObject.
121
122         If @a data is NULL, create a jsonObject of type JSON_NULL.  Otherwise create
123         a jsonObject of type JSON_STRING, containing the specified string.
124
125         The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
126 */
127 jsonObject* jsonNewObject(const char* data) {
128
129         jsonObject* o;
130
131         // Allocate a jsonObject; from the free list if possible,
132         // or from the heap if necessary.
133         if( freeObjList ) {
134                 o = (jsonObject*) freeObjList;
135                 freeObjList = freeObjList->next;
136         unusedObjRelease++;
137         currentListLen--;
138         } else {
139                 OSRF_MALLOC( o, sizeof(jsonObject) );
140         mallocObjCreate++;
141     }
142
143         o->size = 0;
144         o->classname = NULL;
145         o->parent = NULL;
146
147         if(data) {
148                 o->type = JSON_STRING;
149                 o->value.s = strdup(data);
150         } else {
151                 o->type = JSON_NULL;
152                 o->value.s = NULL;
153         }
154
155         return o;
156 }
157
158 /**
159         @brief Create a jsonObject, optionally containing a formatted string.
160         @param data Pointer to a printf-style format string; may be NULL.  Subsequent parameters,
161         if any, will be formatted and inserted into the resulting string.
162         @return Pointer to a newly created jsonObject.
163
164         If @a data is NULL, create a jsonObject of type JSON_NULL, and ignore any extra parameters.
165         Otherwise create a jsonObject of type JSON_STRING, containing the formatted string.
166
167         The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
168  */
169 jsonObject* jsonNewObjectFmt(const char* data, ...) {
170
171         jsonObject* o;
172
173         if( freeObjList ) {
174                 o = (jsonObject*) freeObjList;
175                 freeObjList = freeObjList->next;
176         unusedObjRelease++;
177         currentListLen--;
178         } else {
179                 OSRF_MALLOC( o, sizeof(jsonObject) );
180         mallocObjCreate++;
181     }
182
183         o->size = 0;
184         o->classname = NULL;
185         o->parent = NULL;
186
187         if(data) {
188                 VA_LIST_TO_STRING(data);
189                 o->type = JSON_STRING;
190                 o->value.s = strdup(VA_BUF);
191         }
192         else {
193                 o->type = JSON_NULL;
194                 o->value.s = NULL;
195         }
196         
197         return o;
198 }
199
200 /**
201         @brief Create a new jsonObject of type JSON_NUMBER.
202         @param num The number to store in the jsonObject.
203         @return Pointer to the newly created jsonObject.
204
205         The number is stored internally as a character string, as formatted by
206         doubleToString().
207
208         The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
209 */
210 jsonObject* jsonNewNumberObject( double num ) {
211         jsonObject* o = jsonNewObject(NULL);
212         o->type = JSON_NUMBER;
213         o->value.s = doubleToString( num );
214         return o;
215 }
216
217 /**
218         @brief Create a new jsonObject of type JSON_NUMBER from a numeric string.
219         @param numstr Pointer to a numeric character string.
220         @return Pointer to a newly created jsonObject containing the numeric value, or NULL
221                 if the string is not numeric.
222
223         The jsonIsNumeric() function determines whether the input string is numeric.
224
225         The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
226 */
227 jsonObject* jsonNewNumberStringObject( const char* numstr ) {
228         if( !numstr )
229                 numstr = "0";
230         else if( !jsonIsNumeric( numstr ) )
231                 return NULL;
232
233         jsonObject* o = jsonNewObject(NULL);
234         o->type = JSON_NUMBER;
235         o->value.s = strdup( numstr );
236         return o;
237 }
238
239 /**
240         @brief Create a new jsonObject of type JSON_BOOL, with a specified boolean value.
241         @param val An int used as a boolean value.
242         @return Pointer to the new jsonObject.
243
244         Zero represents false, and non-zero represents true, according to the usual convention.
245         In practice the value of @a val is stored unchanged, but the calling code should not try to
246         take advantage of that fact, because future versions may behave differently.
247
248         The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
249 */
250 jsonObject* jsonNewBoolObject(int val) {
251     jsonObject* o = jsonNewObject(NULL);
252     o->type = JSON_BOOL;
253     jsonSetBool(o, val);
254     return o;
255 }
256
257 /**
258         @brief Create a new jsonObject of a specified type, with a default value.
259         @param type One of the 6 JSON types, as specified by the JSON_* macros.
260         @return Pointer to the new jsonObject.
261
262         An invalid type parameter will go unnoticed, and may lead to unpleasantness.
263
264         The default value is equivalent to an empty string (for a JSON_STRING), zero (for a
265         JSON_NUMBER), an empty hash (for a JSON_HASH), an empty array (for a JSON_ARRAY), or
266         false (for a JSON_BOOL).  A JSON_NULL, of course, has no value, but can still be
267         useful.
268
269         The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
270 */
271 jsonObject* jsonNewObjectType(int type) {
272         jsonObject* o = jsonNewObject(NULL);
273         o->type = type;
274         if( JSON_BOOL == type )
275                 o->value.b = 0;
276         return o;
277 }
278
279 /**
280         @brief Free a jsonObject and everything in it.
281         @param o Pointer to the jsonObject to be freed.
282
283         Any jsonObjects stored inside the jsonObject (in hashes or arrays) will be freed as
284         well, and so one, recursively.
285 */
286 void jsonObjectFree( jsonObject* o ) {
287
288         if(!o || o->parent) return;
289         free(o->classname);
290
291         switch(o->type) {
292                 case JSON_HASH          : osrfHashFree(o->value.h); break;
293                 case JSON_ARRAY : osrfListFree(o->value.l); break;
294                 case JSON_STRING        : free(o->value.s); break;
295                 case JSON_NUMBER        : free(o->value.s); break;
296         }
297
298         // Stick the old jsonObject onto a free list
299         // for potential reuse
300         
301         unusedObj* unused = (unusedObj*) o;
302         unused->next = freeObjList;
303         freeObjList = unused;
304
305     unusedObjCapture++;
306     currentListLen++;
307     if (unusedObjCapture > 1 && !(unusedObjCapture % 1000))
308         osrfLogDebug( OSRF_LOG_MARK, "Objects malloc()'d: %d, "
309                         "Reusable objects captured: %d, Objects reused: %d, "
310                         "Current List Length: %d",
311                         mallocObjCreate, unusedObjCapture, unusedObjRelease, currentListLen );
312 }
313
314 /**
315         @brief Free a jsonObject through a void pointer.
316         @param key Not used.
317         @param item Pointer to the jsonObject to be freed, cast to a void pointer.
318
319         This function is a callback for freeing jsonObjects stored in an osrfHash.
320 */
321 static void _jsonFreeHashItem(char* key, void* item){
322         if(!item) return;
323         jsonObject* o = (jsonObject*) item;
324         o->parent = NULL; /* detach the item */
325         jsonObjectFree(o);
326 }
327
328 /**
329         @brief Free a jsonObject through a void pointer.
330         @param item Pointer to the jsonObject to be freed, cast to a void pointer.
331
332         This function is a callback for freeing jsonObjects stored in an osrfList.
333  */
334 static void _jsonFreeListItem(void* item){
335         if(!item) return;
336         jsonObject* o = (jsonObject*) item;
337         o->parent = NULL; /* detach the item */
338         jsonObjectFree(o);
339 }
340
341 /**
342         @brief Assign a boolean value to a jsonObject of type JSON_BOOL.
343         @param bl Pointer to the jsonObject.
344         @param val The boolean value to be applied, encoded as an int.
345
346         If the jsonObject is not already of type JSON_BOOL, it is converted to one, and
347         any previous contents are freed.
348
349         Zero represents false, and non-zero represents true, according to the usual convention.
350         In practice the value of @a val is stored unchanged, but the calling code should not try to
351         take advantage of that fact, because future versions may behave differently.
352 */
353 void jsonSetBool(jsonObject* bl, int val) {
354     if(!bl) return;
355     JSON_INIT_CLEAR(bl, JSON_BOOL);
356     bl->value.b = val;
357 }
358
359 /**
360         @brief Insert one jsonObject into a jsonObect of type JSON_ARRAY, at the end of the array.
361         @param o Pointer to the outer jsonObject that will receive additional payload.
362         @param newo Pointer to the inner jsonObject, or NULL.
363         @return The number of jsonObjects directly subordinate to the outer jsonObject,
364                 or -1 for error.
365
366         If the pointer to the outer jsonObject is NULL, jsonObjectPush returns -1.
367
368         If the outer jsonObject is not already of type JSON_ARRAY, it is converted to one, and
369         any previous contents are freed.
370
371         If the pointer to the inner jsonObject is NULL, jsonObjectPush creates a new jsonObject
372         of type JSON_NULL, and appends it to the array.
373 */
374 unsigned long jsonObjectPush(jsonObject* o, jsonObject* newo) {
375     if(!o) return -1;
376     if(!newo) newo = jsonNewObject(NULL);
377         JSON_INIT_CLEAR(o, JSON_ARRAY);
378         newo->parent = o;
379         osrfListPush( o->value.l, newo );
380         o->size = o->value.l->size;
381         return o->size;
382 }
383
384 /**
385         @brief Insert a jsonObject at a specified position in a jsonObject of type JSON_ARRAY.
386         @param dest Pointer to the outer jsonObject that will receive the new payload.
387         @param index A zero-based subscript specifying the position of the new jsonObject.
388         @param newObj Pointer to the new jsonObject to be inserted, or NULL.
389         @return The size of the internal osrfList where the array is stored, or -1 on error.
390
391         If @a dest is NULL, jsonObjectSetIndex returns -1.
392
393         If the outer jsonObject is not already of type JSON_ARRAY, it is converted to one, and
394         any previous contents are freed.
395
396         If @a newObj is NULL, jsonObject creates a new jsonObject of type JSON_NULL and
397         inserts it into the array.
398
399         If there is already a jsonObject at the specified location, it is freed and replaced.
400
401         Depending on the placement of the inner jsonObject, it may leave unoccupied holes in the
402         array that are included in the reported size.  As a result of this and other peculiarities
403         of the underlying osrfList within the jsonObject, the reported size may not reflect the
404         number of jsonObjects in the array.  See osrf_list.c for further details.
405 */
406 unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj) {
407         if(!dest) return -1;
408         if(!newObj) newObj = jsonNewObject(NULL);
409         JSON_INIT_CLEAR(dest, JSON_ARRAY);
410         newObj->parent = dest;
411         osrfListSet( dest->value.l, newObj, index );
412         dest->size = dest->value.l->size;
413         return dest->value.l->size;
414 }
415
416 /**
417         @brief Insert a jsonObject into a jsonObject of type JSON_HASH, for a specified key.
418         @param o Pointer to the outer jsonObject that will receive the new payload.
419         @param key Pointer to a string that will serve as the key.
420         @param newo Pointer to the new jsonObject that will be inserted, or NULL.
421         @return The number of items stored in the first level of the hash.
422
423         If the @a o parameter is NULL, jsonObjectSetKey returns -1.
424
425         If the outer jsonObject is not already of type JSON_HASH, it will be converted to one,
426         and any previous contents will be freed.
427
428         If @a key is NULL, jsonObjectSetKey returns the size of the hash without doing anything
429         (apart from possibly converting the outer jsonObject as described above).
430
431         If @a newo is NULL, jsonObjectSetKey creates a new jsonObject of type JSON_NULL and inserts
432         it with the specified key.
433
434         If a previous jsonObject is already stored with the same key, it is freed and replaced.
435 */
436 unsigned long jsonObjectSetKey( jsonObject* o, const char* key, jsonObject* newo) {
437     if(!o) return -1;
438     if(!newo) newo = jsonNewObject(NULL);
439         JSON_INIT_CLEAR(o, JSON_HASH);
440         newo->parent = o;
441         osrfHashSet( o->value.h, newo, key );
442         o->size = osrfHashGetCount(o->value.h);
443         return o->size;
444 }
445
446 /**
447         @brief From a jsonObject of type JSON_HASH, find the inner jsonObject for a specified key.
448         @param obj Pointer to the outer jsonObject.
449         @param key The key for the associated item to be found.
450         @return A pointer to the inner jsonObject, if found, or NULL if not.
451
452         Returns NULL if either parameter is NULL, or if @a obj points to a jsonObject not of type
453         JSON_HASH, or if the specified key is not found in the outer jsonObject.
454
455         The returned pointer (if not NULL) points to the interior of the outer jsonObject.  The
456         calling code should @em not try to free it, but it may change its contents.
457 */
458 jsonObject* jsonObjectGetKey( jsonObject* obj, const char* key ) {
459         if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
460         return osrfHashGet( obj->value.h, key);
461 }
462
463 /**
464         @brief From a jsonObject of type JSON_HASH, find the inner jsonObject for a specified key.
465         @param obj Pointer to the outer jsonObject.
466         @param key The key for the associated item to be found.
467         @return A pointer to the inner jsonObject, if found, or NULL if not.
468
469         This function is identical to jsonObjectGetKey(), except that the outer jsonObject may be
470         const, and the pointer returned points to const.  As a result, the calling code is put on
471         notice that it should not try to modify the contents of the inner jsonObject.
472  */
473 const jsonObject* jsonObjectGetKeyConst( const jsonObject* obj, const char* key ) {
474         if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
475         return osrfHashGet( obj->value.h, key);
476 }
477
478 /**
479         @brief Recursively traverse a jsonObject, translating it into a JSON string.
480         @param obj Pointer to the jsonObject to be translated.
481         @param buf Pointer to a growing_buffer that will receive the JSON string.
482         @param do_classname Boolean; if true, expand class names.
483         @param second_pass Boolean; should always be false except for some recursive calls.
484  
485         If @a do_classname is true, expand any class names, as described in the discussion of
486         jsonObjectToJSON().
487
488         @a second_pass should always be false except for some recursive calls.  It is used
489         when expanding classnames, to distinguish between the first and second passes
490         through a given node.
491 */
492 static void add_json_to_buffer( const jsonObject* obj,
493         growing_buffer * buf, int do_classname, int second_pass ) {
494
495     if(NULL == obj) {
496         OSRF_BUFFER_ADD(buf, "null");
497         return;
498     }
499
500         if( obj->classname && do_classname )
501         {
502                 if( second_pass )
503                         second_pass = 0;
504                 else
505                 {
506                         // Pretend we see an extra layer of JSON_HASH
507                         
508                         OSRF_BUFFER_ADD( buf, "{\"" );
509                         OSRF_BUFFER_ADD( buf, JSON_CLASS_KEY );
510                         OSRF_BUFFER_ADD( buf, "\":\"" );
511                         OSRF_BUFFER_ADD( buf, obj->classname );
512                         OSRF_BUFFER_ADD( buf, "\",\"" );
513                         OSRF_BUFFER_ADD( buf, JSON_DATA_KEY );
514                         OSRF_BUFFER_ADD( buf, "\":" );
515                         add_json_to_buffer( obj, buf, 1, 1 );
516                         buffer_add_char( buf, '}' );
517                         return;
518                 }
519         }
520
521         switch(obj->type) {
522
523                 case JSON_BOOL :
524                         if(obj->value.b) OSRF_BUFFER_ADD(buf, "true"); 
525                         else OSRF_BUFFER_ADD(buf, "false"); 
526                         break;
527
528         case JSON_NUMBER: {
529             if(obj->value.s) OSRF_BUFFER_ADD( buf, obj->value.s );
530             else OSRF_BUFFER_ADD_CHAR( buf, '0' );
531             break;
532         }
533
534                 case JSON_NULL:
535                         OSRF_BUFFER_ADD(buf, "null");
536                         break;
537
538                 case JSON_STRING:
539                         OSRF_BUFFER_ADD_CHAR(buf, '"');
540                         buffer_append_utf8(buf, obj->value.s);
541                         OSRF_BUFFER_ADD_CHAR(buf, '"');
542                         break;
543                         
544                 case JSON_ARRAY: {
545                         OSRF_BUFFER_ADD_CHAR(buf, '[');
546                         if( obj->value.l ) {
547                                 int i;
548                                 for( i = 0; i != obj->value.l->size; i++ ) {
549                                         if(i > 0) OSRF_BUFFER_ADD(buf, ",");
550                                         add_json_to_buffer(
551                                                 OSRF_LIST_GET_INDEX(obj->value.l, i), buf, do_classname, second_pass );
552                                 }
553                         }
554                         OSRF_BUFFER_ADD_CHAR(buf, ']');
555                         break;
556                 }
557
558                 case JSON_HASH: {
559         
560                         OSRF_BUFFER_ADD_CHAR(buf, '{');
561                         osrfHashIterator* itr = osrfNewHashIterator(obj->value.h);
562                         jsonObject* item;
563                         int i = 0;
564
565                         while( (item = osrfHashIteratorNext(itr)) ) {
566                                 if(i++ > 0) OSRF_BUFFER_ADD_CHAR(buf, ',');
567                                 OSRF_BUFFER_ADD_CHAR(buf, '"');
568                                 buffer_append_utf8(buf, osrfHashIteratorKey(itr));
569                                 OSRF_BUFFER_ADD(buf, "\":");
570                                 add_json_to_buffer( item, buf, do_classname, second_pass );
571                         }
572
573                         osrfHashIteratorFree(itr);
574                         OSRF_BUFFER_ADD_CHAR(buf, '}');
575                         break;
576                 }
577         }
578 }
579
580 /**
581         @brief Translate a jsonObject into a JSON string, without expanding class names.
582         @param obj Pointer to the jsonObject to be translated.
583         @return A pointer to a newly allocated string containing the JSON.
584
585         The calling code is responsible for freeing the resulting string.
586 */
587 char* jsonObjectToJSONRaw( const jsonObject* obj ) {
588         if(!obj) return NULL;
589         growing_buffer* buf = buffer_init(32);
590         add_json_to_buffer( obj, buf, 0, 0 );
591         return buffer_release( buf );
592 }
593
594 /**
595         @brief Translate a jsonObject into a JSON string, with expansion of class names.
596         @param obj Pointer to the jsonObject to be translated.
597         @return A pointer to a newly allocated string containing the JSON.
598
599         At every level, any jsonObject containing a class name will be translated as if it were
600         under an extra layer of JSON_HASH, with JSON_CLASS_KEY as the key for the class name and
601         JSON_DATA_KEY as the key for the jsonObject.
602
603         The calling code is responsible for freeing the resulting string.
604  */
605 char* jsonObjectToJSON( const jsonObject* obj ) {
606         if(!obj) return NULL;
607         growing_buffer* buf = buffer_init(32);
608         add_json_to_buffer( obj, buf, 1, 0 );
609         return buffer_release( buf );
610 }
611
612 /**
613         @brief Create a new jsonIterator for traversing a specified jsonObject.
614         @param obj Pointer to the jsonObject to be traversed.
615         @return A pointer to the newly allocated jsonIterator, or NULL upon error.
616
617         jsonNewIterator returns NULL if @a obj is NULL.
618
619         The new jsonIterator does not point to any particular position within the jsonObject to
620         be traversed.  The next call to jsonIteratorNext() will position it at the beginning.
621
622         The calling code is responsible for freeing the jsonIterator by calling jsonIteratorFree().
623 */
624 jsonIterator* jsonNewIterator(const jsonObject* obj) {
625         if(!obj) return NULL;
626         jsonIterator* itr;
627         OSRF_MALLOC(itr, sizeof(jsonIterator));
628
629         itr->obj    = (jsonObject*) obj;
630         itr->index  = 0;
631         itr->key    = NULL;
632
633         if( obj->type == JSON_HASH )
634                 itr->hashItr = osrfNewHashIterator(obj->value.h);
635         else
636                 itr->hashItr = NULL;
637         
638         return itr;
639 }
640
641 /**
642         @brief Free a jsonIterator and everything in it.
643         @param itr Pointer to the jsonIterator to be freed.
644 */
645 void jsonIteratorFree(jsonIterator* itr) {
646         if(!itr) return;
647         osrfHashIteratorFree(itr->hashItr);
648         free(itr);
649 }
650
651 /**
652         @brief Advance a jsonIterator to the next position within a jsonObject.
653         @param itr Pointer to the jsonIterator to be advanced.
654         @return A Pointer to the next jsonObject within the jsonObject being traversed; or NULL.
655
656         If the jsonObject being traversed is of type JSON_HASH, jsonIteratorNext returns a pointer
657         to the next jsonObject within the internal osrfHash.  The associated key string is available
658         via the pointer member itr->key.
659
660         If the jsonObject being traversed is of type JSON_ARRAY, jsonIteratorNext returns a pointer
661         to the next jsonObject within the internal osrfList.
662
663         In either case, the jsonIterator remains at the same level within the jsonObject that it is
664         traversing.  It does @em not descend to traverse deeper levels recursively.
665
666         If there is no next jsonObject within the jsonObject being traversed, jsonIteratorNext
667         returns NULL.  It also returns NULL if @a itr is NULL, or if the jsonIterator is
668         detectably corrupted, or if the jsonObject to be traversed is of a type other than
669         JSON_HASH or JSON_ARRAY.
670
671         Once jsonIteratorNext has reached the end of the jsonObject that it is traversing,
672         subsequent calls using the same iterator will continue to return NULL.  There is no available
673         function to start over at the beginning.
674
675         The pointer returned, if not NULL, points to an internal element of the jsonObject being
676         traversed.  The calling code should @em not try to free it, but it may modify its contents.
677 */
678 jsonObject* jsonIteratorNext(jsonIterator* itr) {
679         if(!(itr && itr->obj)) return NULL;
680         if( itr->obj->type == JSON_HASH ) {
681                 if(!itr->hashItr)
682                         return NULL;
683
684                 jsonObject* item = osrfHashIteratorNext(itr->hashItr);
685                 if( item )
686                         itr->key = osrfHashIteratorKey(itr->hashItr);
687                 else
688                         itr->key = NULL;
689                 return item;
690         } else {
691                 return jsonObjectGetIndex( itr->obj, itr->index++ );
692         }
693 }
694
695 /**
696         @brief Determine whether a jsonIterator is positioned at the last element of a jsonObject.
697         @param itr Pointer to the jsonIterator whose position is to be tested.
698         @return An int, as boolean: 0 if the iterator is positioned at the end, or 1 if it isn't.
699
700         If the jsonIterator is positioned where a call to jsonIteratorNext() would return NULL,
701         then jsonIteratorHasNext returns 0.  Otherwise it returns 1.
702 */
703 int jsonIteratorHasNext(const jsonIterator* itr) {
704         if(!(itr && itr->obj)) return 0;
705         if( itr->obj->type == JSON_HASH )
706                 return osrfHashIteratorHasNext( itr->hashItr );
707         return (itr->index < itr->obj->size) ? 1 : 0;
708 }
709
710 /**
711         @brief Fetch a pointer to a specified element within a jsonObject of type JSON_ARRAY.
712         @param obj Pointer to the outer jsonObject.
713         @param index A zero-based index identifying the element to be fetched.
714         @return A pointer to the element at the specified location, if any, or NULL.
715
716         The return value is NULL if @a obj is null, or if the outer jsonObject is not of type
717         JSON_ARRAY, or if there is no element at the specified location.
718
719         If not NULL, the pointer returned points to an element within the outer jsonObject.  The
720         calling code should @em not try to free it, but it may modify its contents.
721 */
722 jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index ) {
723         if(!obj) return NULL;
724         return (obj->type == JSON_ARRAY) ? 
725         (OSRF_LIST_GET_INDEX(obj->value.l, index)) : NULL;
726 }
727
728 /**
729         @brief Remove a specified element from a jsonObject of type JSON_ARRAY.
730         @param dest Pointer to the jsonObject from which the element is to be removed.
731         @param index A zero-based index identifying the element to be removed.
732         @return The number of elements remaining at the top level, or -1 upon error.
733
734         The return value is -1 if @a dest is NULL, or if it points to a jsonObject not of type
735         JSON_ARRAY.  Otherwise it reflects the number of elements remaining in the top level of
736         the outer jsonObject, not counting any at lower levels.
737 */
738 unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index) {
739         if( dest && dest->type == JSON_ARRAY ) {
740                 osrfListRemove(dest->value.l, index);
741                 return dest->value.l->size;
742         }
743         return -1;
744 }
745
746 /**
747         @brief Extract a specified element from a jsonObject of type JSON_ARRAY.
748         @param dest Pointer to the jsonObject from which the element is to be extracted.
749         @param index A zero-based index identifying the element to be extracted.
750         @return A pointer to the extracted element, if successful; otherwise NULL.
751
752         THe return value is NULL if @a dest is NULL, or if it points to a jsonObject not of type
753         JSON_ARRAY, or if there is no element at the specified location.
754
755         Otherwise, the calling code assumes ownership of the jsonObject to which the return value
756         points, and is responsible for freeing it by calling jsonObjectFree().  The original outer
757         jsonObject remains unchanged except for the removal of the specified element.
758
759         This function is sijmilar to jsonObjectRemoveIndex(), except that it returns a pointer to
760         the removed sub-object instead of destroying it.
761 */
762 jsonObject* jsonObjectExtractIndex(jsonObject* dest, unsigned long index) {
763         if( dest && dest->type == JSON_ARRAY ) {
764                 jsonObject* obj = osrfListExtract(dest->value.l, index);
765                 if( obj )
766                         obj->parent = NULL;
767                 return obj;
768         } else
769                 return NULL;
770 }
771
772 /**
773         @brief Remove an element, specified by key, from a jsonObject of type JSON_HASH.
774         @param dest Pointer to the outer jsonObject from which an element is to be removed.
775         @param key The key for the associated element to be removed.
776         @return 1 if successful, or -1 if not.
777
778         The operation is considered successful if @a dest and @a key are both non-NULL, and
779         @a dest points to a jsonObject of type JSON_HASH, even if the specified key is not found.
780 */
781 unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key) {
782         if( dest && key && dest->type == JSON_HASH ) {
783                 osrfHashRemove(dest->value.h, key);
784                 return 1;
785         }
786         return -1;
787 }
788
789 /**
790         @brief Format a double into a character string.
791         @param num The double to be formatted.
792         @return A newly allocated character string containing the formatted number.
793
794         The number is formatted according to the printf-style format specification "%.30g". and
795         will therefore contain no more than 30 significant digits.
796
797         The calling code is responsible for freeing the resulting string.
798 */
799 char* doubleToString( double num ) {
800
801         char buf[ 64 ];
802         size_t len = snprintf(buf, sizeof( buf ), "%.30g", num) + 1;
803         if( len < sizeof( buf ) )
804                 return strdup( buf );
805         else
806         {
807                 // Need a bigger buffer (should never be necessary)
808                 
809                 char* bigger_buff = safe_malloc( len + 1 );
810                 (void) snprintf(bigger_buff, len + 1, "%.30g", num);
811                 return bigger_buff;
812         }
813 }
814
815 /**
816         @brief Fetch a pointer to the string stored in a jsonObject, if any.
817         @param obj Pointer to the jsonObject.
818         @return Pointer to the string stored internally, or NULL.
819
820         If @a obj points to a jsonObject of type JSON_STRING or JSON_NUMBER, the returned value
821         points to the string stored internally (a numeric string in the case of a JSON_NUMBER).
822         Otherwise the returned value is NULL.
823 */
824 const char* jsonObjectGetString(const jsonObject* obj) {
825         if(obj)
826         {
827                 if( obj->type == JSON_STRING )
828                         return obj->value.s;
829                 else if( obj->type == JSON_NUMBER )
830                         return obj->value.s ? obj->value.s : "0";
831                 else
832                         return NULL;
833         }
834         else
835                 return NULL;
836 }
837
838 /**
839         @brief Translate a jsonObject to a double.
840         @param @obj Pointer to the jsonObject.
841         @return The numeric value stored in the jsonObject.
842
843         If @a obj is NULL, or if it points to a jsonObject not of type JSON_NUMBER, the value
844         returned is zero.
845 */
846 double jsonObjectGetNumber( const jsonObject* obj ) {
847         return (obj && obj->type == JSON_NUMBER && obj->value.s)
848                         ? strtod( obj->value.s, NULL ) : 0;
849 }
850
851 /**
852         @brief Store a copy of a specified character string in a jsonObject of type JSON_STRING.
853         @param dest Pointer to the jsonObject in which the string will be stored.
854         @param string Pointer to the string to be stored.
855
856         Both @a dest and @a string must be non-NULL.
857
858         If the jsonObject is not already of type JSON_STRING, it is converted to a JSON_STRING,
859         with any previous contents freed.
860 */
861 void jsonObjectSetString(jsonObject* dest, const char* string) {
862         if(!(dest && string)) return;
863         JSON_INIT_CLEAR(dest, JSON_STRING);
864         dest->value.s = strdup(string);
865 }
866
867 /**
868  Turn a jsonObject into a JSON_NUMBER (if it isn't already one) and store
869  a specified numeric string in it.  If the string is not numeric,
870  store the equivalent of zero, and return an error status.
871 **/
872 /**
873         @brief Store a copy of a numeric character string in a jsonObject of type JSON_NUMBER.
874         @param dest Pointer to the jsonObject in which the number will be stored.
875         @param string Pointer to the numeric string to be stored.
876
877         Both @a dest and @a string must be non-NULL.
878
879         If the jsonObject is not already of type JSON_NUMBER, it is converted to a JSON_STRING,
880         with any previous contents freed.
881
882         If the input string is not numeric as determined by jsonIsNumeric(), the number stored
883         is zero.
884  */
885 int jsonObjectSetNumberString(jsonObject* dest, const char* string) {
886         if(!(dest && string)) return -1;
887         JSON_INIT_CLEAR(dest, JSON_NUMBER);
888
889         if( jsonIsNumeric( string ) ) {
890                 dest->value.s = strdup(string);
891                 return 0;
892         }
893         else {
894                 dest->value.s = NULL;  // equivalent to zero
895                 return -1;
896         }
897 }
898
899 /**
900         @brief Store a number in a jsonObject of type JSON_NUMBER.
901         @param dest Pointer to the jsonObject in which the number will be stored.
902         @param num The number to be stored.
903
904         If the jsonObject is not already of type JSON_NUMBER, it is converted to one, with any
905         previous contents freed.
906 */
907 void jsonObjectSetNumber(jsonObject* dest, double num) {
908         if(!dest) return;
909         JSON_INIT_CLEAR(dest, JSON_NUMBER);
910         dest->value.s = doubleToString( num );
911 }
912
913 /**
914         @brief Assign a class name to a jsonObject.
915         @param dest Pointer to the jsonObject.
916         @param classname Pointer to a string containing the class name.
917
918         Both dest and classname must be non-NULL.
919 */
920 void jsonObjectSetClass(jsonObject* dest, const char* classname ) {
921         if(!(dest && classname)) return;
922         free(dest->classname);
923         dest->classname = strdup(classname);
924 }
925
926 /**
927         @brief Fetch a pointer to the class name of a jsonObject, if any.
928         @param dest Pointer to the jsonObject.
929         @return Pointer to a string containing the class name, if there is one; or NULL.
930
931         If not NULL, the pointer returned points to a string stored internally within the
932         jsonObject.  The calling code should @em not try to free it.
933 */
934 const char* jsonObjectGetClass(const jsonObject* dest) {
935     if(!dest) return NULL;
936     return dest->classname;
937 }
938
939 /**
940         @brief Create a copy of an existing jsonObject, including all internal sub-objects.
941         @param o Pointer to the jsonObject to be copied.
942         @return A pointer to the newly created copy.
943
944         The calling code is responsible for freeing the copy of the original.
945 */
946 jsonObject* jsonObjectClone( const jsonObject* o ) {
947     if(!o) return jsonNewObject(NULL);
948
949     int i;
950     jsonObject* arr; 
951     jsonObject* hash; 
952     jsonIterator* itr;
953     jsonObject* tmp;
954     jsonObject* result = NULL;
955
956     switch(o->type) {
957         case JSON_NULL:
958             result = jsonNewObject(NULL);
959             break;
960         case JSON_STRING:
961             result = jsonNewObject(jsonObjectGetString(o));
962             break;
963         case JSON_NUMBER:
964                         result = jsonNewObject( o->value.s );
965                         result->type = JSON_NUMBER;
966             break;
967         case JSON_BOOL:
968             result = jsonNewBoolObject(jsonBoolIsTrue((jsonObject*) o));
969             break;
970         case JSON_ARRAY:
971             arr = jsonNewObject(NULL);
972             arr->type = JSON_ARRAY;
973             for(i=0; i < o->size; i++) 
974                 jsonObjectPush(arr, jsonObjectClone(jsonObjectGetIndex(o, i)));
975             result = arr;
976             break;
977         case JSON_HASH:
978             hash = jsonNewObject(NULL);
979             hash->type = JSON_HASH;
980             itr = jsonNewIterator(o);
981             while( (tmp = jsonIteratorNext(itr)) )
982                 jsonObjectSetKey(hash, itr->key, jsonObjectClone(tmp));
983             jsonIteratorFree(itr);
984             result = hash;
985             break;
986     }
987
988     jsonObjectSetClass(result, jsonObjectGetClass(o));
989     return result;
990 }
991
992 /**
993         @brief Return the truth or falsity of a jsonObject of type JSON_BOOL.
994         @param boolObj Pointer to the jsonObject.
995         @return 1 or 0, depending on whether the stored boolean is true or false.
996
997         If @a boolObj is NULL, or if it points to a jsonObject not of type JSON_BOOL, the
998         returned value is zero.
999 */
1000 int jsonBoolIsTrue( const jsonObject* boolObj ) {
1001     if( boolObj && boolObj->type == JSON_BOOL && boolObj->value.b )
1002         return 1;
1003     return 0;
1004 }
1005
1006
1007 /**
1008         @brief Create a copy of the string stored in a jsonObject.
1009         @param o Pointer to the jsonObject whose string is to be copied.
1010         @return Pointer to a newly allocated string copied from the jsonObject.
1011
1012         If @a o is NULL, or if it points to a jsonObject not of type JSON_STRING or JSON_NUMBER,
1013         the returned value is NULL.  In the case of a JSON_NUMBER, the string created is numeric.
1014
1015         The calling code is responsible for freeing the newly created string.
1016 */
1017 char* jsonObjectToSimpleString( const jsonObject* o ) {
1018         if(!o) return NULL;
1019
1020         char* value = NULL;
1021
1022         switch( o->type ) {
1023
1024                 case JSON_NUMBER:
1025                         value = strdup( o->value.s ? o->value.s : "0" );
1026                         break;
1027
1028                 case JSON_STRING:
1029                         value = strdup(o->value.s);
1030         }
1031
1032         return value;
1033 }
1034
1035 /**
1036  Return 1 if the string is numeric, otherwise return 0.
1037  This validation follows the rules defined by the grammar at:
1038  http://www.json.org/
1039  **/
1040 /**
1041         @brief Determine whether a specified character string is a valid JSON number.
1042         @param s Pointer to the string to be examined.
1043         @return 1 if the string is numeric, or 0 if not.
1044
1045         This function defines numericity according to JSON rules; see http://json.org/.  This
1046         determination is based purely on the lexical properties of the string.  In particular
1047         there is no guarantee that the number in a numeric string is representable in C as a
1048         long long, a long double, or any other built-in type.
1049
1050         A numeric string consists of:
1051
1052         - An optional leading minus sign (but not a plus sign)
1053         - One or more decimal digits.  The first digit may be a zero only if it is the only
1054           digit to the left of the decimal.
1055         - Optionally, a decimal point followed by one or more decimal digits.
1056         - An optional exponent, consisting of:
1057                 - The letter E, in upper or lower case
1058                 - An optional plus or minus sign
1059                 - One or more decimal digits
1060
1061         See also jsonScrubNumber().
1062 */
1063 int jsonIsNumeric( const char* s ) {
1064
1065         if( !s || !*s ) return 0;
1066
1067         const char* p = s;
1068
1069         // skip leading minus sign, if present (leading plus sign not allowed)
1070
1071         if( '-' == *p )
1072                 ++p;
1073
1074         // There must be at least one digit to the left of the decimal
1075
1076         if( isdigit( (unsigned char) *p ) ) {
1077                 if( '0' == *p++ ) {
1078
1079                         // If the first digit is zero, it must be the
1080                         // only digit to the left of the decimal
1081
1082                         if( isdigit( (unsigned char) *p ) )
1083                                 return 0;
1084                 }
1085                 else {
1086
1087                         // Skip over the following digits
1088
1089                         while( isdigit( (unsigned char) *p ) ) ++p;
1090                 }
1091         }
1092         else
1093                 return 0;
1094
1095         if( !*p )
1096                 return 1;             // integer
1097
1098         if( '.' == *p ) {
1099
1100                 ++p;
1101
1102                 // If there is a decimal point, there must be
1103                 // at least one digit to the right of it
1104
1105                 if( isdigit( (unsigned char) *p ) )
1106                         ++p;
1107                 else
1108                         return 0;
1109
1110                 // skip over contiguous digits
1111
1112                 while( isdigit( (unsigned char) *p ) ) ++p;
1113         }
1114
1115         if( ! *p )
1116                 return 1;  // decimal fraction, no exponent
1117         else if( *p != 'e' && *p != 'E' )
1118                 return 0;  // extra junk, no exponent
1119         else
1120                 ++p;
1121
1122         // If we get this far, we have the beginnings of an exponent.
1123         // Skip over optional sign of exponent.
1124
1125         if( '-' == *p || '+' == *p )
1126                 ++p;
1127
1128         // There must be at least one digit in the exponent
1129         
1130         if( isdigit( (unsigned char) *p ) )
1131                 ++p;
1132         else
1133                 return 0;
1134
1135         // skip over contiguous digits
1136
1137         while( isdigit( (unsigned char) *p ) ) ++p;
1138
1139         if( *p )
1140                 return 0;  // extra junk
1141         else
1142                 return 1;  // number with exponent
1143 }
1144
1145 /**
1146         @brief Edit a string into a valid JSON number, if possible.
1147         @param s Pointer to the string to be edited.
1148         @return A pointer to a newly created numeric string, if possible; otherwise NULL.
1149
1150         JSON has rather exacting requirements about what constitutes a valid numeric string (see
1151         jsonIsNumeric()).  Real-world input may be a bit sloppy.  jsonScrubNumber accepts numeric
1152         strings in a less formal format and reformats them, where possible, according to JSON
1153         rules.  It removes leading white space, a leading plus sign, and extraneous leading zeros.
1154         It adds a leading zero as needed when the absolute value is less than 1.  It also accepts
1155         scientific notation in the form of a bare exponent (e.g. "E-3"), supplying a leading factor
1156         of "1".
1157
1158         If the input string is non-numeric even according to these relaxed rules, the return value
1159         is NULL.
1160
1161         The calling code is responsible for freeing the newly created numeric string.
1162 */
1163 char* jsonScrubNumber( const char* s ) {
1164         if( !s || !*s ) return NULL;
1165
1166         growing_buffer* buf = buffer_init( 64 );
1167
1168         // Skip leading white space, if present
1169
1170         while( isspace( (unsigned char) *s ) ) ++s;
1171
1172         // Skip leading plus sign, if present, but keep a minus
1173
1174         if( '-' == *s )
1175         {
1176                 buffer_add_char( buf, '-' );
1177                 ++s;
1178         }
1179         else if( '+' == *s )
1180                 ++s;
1181
1182         if( '\0' == *s ) {
1183                 // No digits found
1184
1185                 buffer_free( buf );
1186                 return NULL;
1187         }
1188         // Skip any leading zeros
1189
1190         while( '0' == *s ) ++s;
1191
1192         // Capture digits to the left of the decimal,
1193         // and note whether there are any.
1194
1195         int left_digit = 0;  // boolean
1196
1197         if( isdigit( (unsigned char) *s ) ) {
1198                 buffer_add_char( buf, *s++ );
1199                 left_digit = 1;
1200         }
1201         
1202         while( isdigit( (unsigned char) *s  ) )
1203                 buffer_add_char( buf, *s++ );
1204
1205         // Now we expect to see a decimal point,
1206         // an exponent, or end-of-string.
1207
1208         switch( *s )
1209         {
1210                 case '\0' :
1211                         break;
1212                 case '.' :
1213                 {
1214                         // Add a single leading zero, if we need to
1215
1216                         if( ! left_digit )
1217                                 buffer_add_char( buf, '0' );
1218                         buffer_add_char( buf, '.' );
1219                         ++s;
1220
1221                         if( ! left_digit && ! isdigit( (unsigned char) *s ) )
1222                         {
1223                                 // No digits on either side of decimal
1224
1225                                 buffer_free( buf );
1226                                 return NULL;
1227                         }
1228
1229                         // Collect digits to right of decimal
1230
1231                         while( isdigit( (unsigned char) *s ) )
1232                                 buffer_add_char( buf, *s++ );
1233
1234                         break;
1235                 }
1236                 case 'e' :
1237                 case 'E' :
1238
1239                         // Exponent; we'll deal with it later, but
1240                         // meanwhile make sure we have something
1241                         // to its left
1242
1243                         if( ! left_digit )
1244                                 buffer_add_char( buf, '1' );
1245                         break;
1246                 default :
1247
1248                         // Unexpected character; bail out
1249
1250                         buffer_free( buf );
1251                         return NULL;
1252         }
1253
1254         if( '\0' == *s )    // Are we done yet?
1255                 return buffer_release( buf );
1256
1257         if( 'e' != *s && 'E' != *s ) {
1258
1259                 // Unexpected character: bail out
1260
1261                 buffer_free( buf );
1262                 return NULL;
1263         }
1264
1265         // We have an exponent.  Load the e or E,
1266         // and the sign if there is one.
1267
1268         buffer_add_char( buf, *s++ );
1269
1270         if( '+' == *s || '-' == *s )
1271                 buffer_add_char( buf, *s++ );
1272
1273         // Collect digits of the exponent
1274
1275         while( isdigit( (unsigned char) *s ) )
1276                 buffer_add_char( buf, *s++ );
1277
1278         // There better not be anything left
1279
1280         if( *s ) {
1281                 buffer_free( buf );
1282                 return NULL;
1283         }
1284
1285         return buffer_release( buf );
1286 }