]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/libopensrf/osrf_json_object.c
1. Add doxygen markup for documentation.
[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 returned NULL, subsequent calls using the same iterator will
672         continue to return NULL.  There is no available function to start over at the beginning.
673
674         The pointer returned, if not NULL, points to an internal element of the jsonObject being
675         traversed.  The calling code should @em not try to free it, but it may modify its contents.
676 */
677 jsonObject* jsonIteratorNext(jsonIterator* itr) {
678         if(!(itr && itr->obj)) return NULL;
679         if( itr->obj->type == JSON_HASH ) {
680                 if(!itr->hashItr)
681                         return NULL;
682
683                 jsonObject* item = osrfHashIteratorNext(itr->hashItr);
684                 if( item )
685                         itr->key = osrfHashIteratorKey(itr->hashItr);
686                 else
687                         itr->key = NULL;
688                 return item;
689         } else {
690                 return jsonObjectGetIndex( itr->obj, itr->index++ );
691         }
692 }
693
694 /**
695         @brief Determine whether a jsonIterator is positioned at the last element of a jsonObject.
696         @param itr Pointer to the jsonIterator whose position is to be tested.
697         @return An int, as boolean: 0 if the iterator is positioned at the end, or 1 if it isn't.
698
699         If the jsonIterator is positioned where a call to jsonIteratorNext() would return NULL,
700         then jsonIteratorHasNext returns 0.  Otherwise it returns 1.
701 */
702 int jsonIteratorHasNext(const jsonIterator* itr) {
703         if(!(itr && itr->obj)) return 0;
704         if( itr->obj->type == JSON_HASH )
705                 return osrfHashIteratorHasNext( itr->hashItr );
706         return (itr->index < itr->obj->size) ? 1 : 0;
707 }
708
709 /**
710         @brief Fetch a pointer to a specified element within a jsonObject of type JSON_ARRAY.
711         @param obj Pointer to the outer jsonObject.
712         @param index A zero-based index identifying the element to be fetched.
713         @return A pointer to the element at the specified location, if any, or NULL.
714
715         The return value is NULL if @a obj is null, or if the outer jsonObject is not of type
716         JSON_ARRAY, or if there is no element at the specified location.
717
718         If not NULL, the pointer returned points to an element within the outer jsonObject.  The
719         calling code should @em not try to free it, but it may modify its contents.
720 */
721 jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index ) {
722         if(!obj) return NULL;
723         return (obj->type == JSON_ARRAY) ? 
724         (OSRF_LIST_GET_INDEX(obj->value.l, index)) : NULL;
725 }
726
727 /**
728         @brief Remove a specified element from a jsonObject of type JSON_ARRAY.
729         @param dest Pointer to the jsonObject from which the element is to be removed.
730         @param index A zero-based index identifying the element to be removed.
731         @return The number of elements remaining at the top level, or -1 upon error.
732
733         The return value is -1 if @a dest is NULL, or if it points to a jsonObject not of type
734         JSON_ARRAY.  Otherwise it reflects the number of elements remaining in the top level of
735         the outer jsonObject, not counting any at lower levels.
736 */
737 unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index) {
738         if( dest && dest->type == JSON_ARRAY ) {
739                 osrfListRemove(dest->value.l, index);
740                 return dest->value.l->size;
741         }
742         return -1;
743 }
744
745 /**
746         @brief Extract a specified element from a jsonObject of type JSON_ARRAY.
747         @param dest Pointer to the jsonObject from which the element is to be extracted.
748         @param index A zero-based index identifying the element to be extracted.
749         @return A pointer to the extracted element, if successful; otherwise NULL.
750
751         THe return value is NULL if @a dest is NULL, or if it points to a jsonObject not of type
752         JSON_ARRAY, or if there is no element at the specified location.
753
754         Otherwise, the calling code assumes ownership of the jsonObject to which the return value
755         points, and is responsible for freeing it by calling jsonObjectFree().  The original outer
756         jsonObject remains unchanged except for the removal of the specified element.
757
758         This function is sijmilar to jsonObjectRemoveIndex(), except that it returns a pointer to
759         the removed sub-object instead of destroying it.
760 */
761 jsonObject* jsonObjectExtractIndex(jsonObject* dest, unsigned long index) {
762         if( dest && dest->type == JSON_ARRAY ) {
763                 jsonObject* obj = osrfListExtract(dest->value.l, index);
764                 if( obj )
765                         obj->parent = NULL;
766                 return obj;
767         } else
768                 return NULL;
769 }
770
771 /**
772         @brief Remove an element, specified by key, from a jsonObject of type JSON_HASH.
773         @param dest Pointer to the outer jsonObject from which an element is to be removed.
774         @param key The key for the associated element to be removed.
775         @return 1 if successful, or -1 if not.
776
777         The operation is considered successful if @a dest and @a key are both non-NULL, and
778         @a dest points to a jsonObject of type JSON_HASH, even if the specified key is not found.
779 */
780 unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key) {
781         if( dest && key && dest->type == JSON_HASH ) {
782                 osrfHashRemove(dest->value.h, key);
783                 return 1;
784         }
785         return -1;
786 }
787
788 /**
789         @brief Format a double into a character string.
790         @param num The double to be formatted.
791         @return A newly allocated character string containing the formatted number.
792
793         The number is formatted according to the printf-style format specification "%.30g". and
794         will therefore contain no more than 30 significant digits.
795
796         The calling code is responsible for freeing the resulting string.
797 */
798 char* doubleToString( double num ) {
799
800         char buf[ 64 ];
801         size_t len = snprintf(buf, sizeof( buf ), "%.30g", num) + 1;
802         if( len < sizeof( buf ) )
803                 return strdup( buf );
804         else
805         {
806                 // Need a bigger buffer (should never be necessary)
807                 
808                 char* bigger_buff = safe_malloc( len + 1 );
809                 (void) snprintf(bigger_buff, len + 1, "%.30g", num);
810                 return bigger_buff;
811         }
812 }
813
814 /**
815         @brief Fetch a pointer to the string stored in a jsonObject, if any.
816         @param obj Pointer to the jsonObject.
817         @return Pointer to the string stored internally, or NULL.
818
819         If @a obj points to a jsonObject of type JSON_STRING or JSON_NUMBER, the returned value
820         points to the string stored internally (a numeric string in the case of a JSON_NUMBER).
821         Otherwise the returned value is NULL.
822
823         The returned pointer should be treated as a pointer to const.  In particular it should
824         @em not be freed.  In a future release, the returned pointer may indeed be a pointer
825         to const.
826 */
827 char* jsonObjectGetString(const jsonObject* obj) {
828         if(obj)
829         {
830                 if( obj->type == JSON_STRING )
831                         return obj->value.s;
832                 else if( obj->type == JSON_NUMBER )
833                         return obj->value.s ? obj->value.s : "0";
834                 else
835                         return NULL;
836         }
837         else
838                 return NULL;
839 }
840
841 /**
842         @brief Translate a jsonObject to a double.
843         @param @obj Pointer to the jsonObject.
844         @return The numeric value stored in the jsonObject.
845
846         If @a obj is NULL, or if it points to a jsonObject not of type JSON_NUMBER, the value
847         returned is zero.
848 */
849 double jsonObjectGetNumber( const jsonObject* obj ) {
850         return (obj && obj->type == JSON_NUMBER && obj->value.s)
851                         ? strtod( obj->value.s, NULL ) : 0;
852 }
853
854 /**
855         @brief Store a copy of a specified character string in a jsonObject of type JSON_STRING.
856         @param dest Pointer to the jsonObject in which the string will be stored.
857         @param string Pointer to the string to be stored.
858
859         Both @a dest and @a string must be non-NULL.
860
861         If the jsonObject is not already of type JSON_STRING, it is converted to a JSON_STRING,
862         with any previous contents freed.
863 */
864 void jsonObjectSetString(jsonObject* dest, const char* string) {
865         if(!(dest && string)) return;
866         JSON_INIT_CLEAR(dest, JSON_STRING);
867         dest->value.s = strdup(string);
868 }
869
870 /**
871  Turn a jsonObject into a JSON_NUMBER (if it isn't already one) and store
872  a specified numeric string in it.  If the string is not numeric,
873  store the equivalent of zero, and return an error status.
874 **/
875 /**
876         @brief Store a copy of a numeric character string in a jsonObject of type JSON_NUMBER.
877         @param dest Pointer to the jsonObject in which the number will be stored.
878         @param string Pointer to the numeric string to be stored.
879
880         Both @a dest and @a string must be non-NULL.
881
882         If the jsonObject is not already of type JSON_NUMBER, it is converted to a JSON_STRING,
883         with any previous contents freed.
884
885         If the input string is not numeric as determined by jsonIsNumeric(), the number stored
886         is zero.
887  */
888 int jsonObjectSetNumberString(jsonObject* dest, const char* string) {
889         if(!(dest && string)) return -1;
890         JSON_INIT_CLEAR(dest, JSON_NUMBER);
891
892         if( jsonIsNumeric( string ) ) {
893                 dest->value.s = strdup(string);
894                 return 0;
895         }
896         else {
897                 dest->value.s = NULL;  // equivalent to zero
898                 return -1;
899         }
900 }
901
902 /**
903         @brief Store a number in a jsonObject of type JSON_NUMBER.
904         @param dest Pointer to the jsonObject in which the number will be stored.
905         @param num The number to be stored.
906
907         If the jsonObject is not already of type JSON_NUMBER, it is converted to one, with any
908         previous contents freed.
909 */
910 void jsonObjectSetNumber(jsonObject* dest, double num) {
911         if(!dest) return;
912         JSON_INIT_CLEAR(dest, JSON_NUMBER);
913         dest->value.s = doubleToString( num );
914 }
915
916 /**
917         @brief Assign a class name to a jsonObject.
918         @param dest Pointer to the jsonObject.
919         @param classname Pointer to a string containing the class name.
920
921         Both dest and classname must be non-NULL.
922 */
923 void jsonObjectSetClass(jsonObject* dest, const char* classname ) {
924         if(!(dest && classname)) return;
925         free(dest->classname);
926         dest->classname = strdup(classname);
927 }
928
929 /**
930         @brief Fetch a pointer to the class name of a jsonObject, if any.
931         @param dest Pointer to the jsonObject.
932         @return Pointer to a string containing the class name, if there is one; or NULL.
933
934         If not NULL, the pointer returned points to a string stored internally within the
935         jsonObject.  The calling code should @em not try to free it.
936 */
937 const char* jsonObjectGetClass(const jsonObject* dest) {
938     if(!dest) return NULL;
939     return dest->classname;
940 }
941
942 /**
943         @brief Create a copy of an existing jsonObject, including all internal sub-objects.
944         @param o Pointer to the jsonObject to be copied.
945         @return A pointer to the newly created copy.
946
947         The calling code is responsible for freeing the copy of the original.
948 */
949 jsonObject* jsonObjectClone( const jsonObject* o ) {
950     if(!o) return jsonNewObject(NULL);
951
952     int i;
953     jsonObject* arr; 
954     jsonObject* hash; 
955     jsonIterator* itr;
956     jsonObject* tmp;
957     jsonObject* result = NULL;
958
959     switch(o->type) {
960         case JSON_NULL:
961             result = jsonNewObject(NULL);
962             break;
963         case JSON_STRING:
964             result = jsonNewObject(jsonObjectGetString(o));
965             break;
966         case JSON_NUMBER:
967                         result = jsonNewObject( o->value.s );
968                         result->type = JSON_NUMBER;
969             break;
970         case JSON_BOOL:
971             result = jsonNewBoolObject(jsonBoolIsTrue((jsonObject*) o));
972             break;
973         case JSON_ARRAY:
974             arr = jsonNewObject(NULL);
975             arr->type = JSON_ARRAY;
976             for(i=0; i < o->size; i++) 
977                 jsonObjectPush(arr, jsonObjectClone(jsonObjectGetIndex(o, i)));
978             result = arr;
979             break;
980         case JSON_HASH:
981             hash = jsonNewObject(NULL);
982             hash->type = JSON_HASH;
983             itr = jsonNewIterator(o);
984             while( (tmp = jsonIteratorNext(itr)) )
985                 jsonObjectSetKey(hash, itr->key, jsonObjectClone(tmp));
986             jsonIteratorFree(itr);
987             result = hash;
988             break;
989     }
990
991     jsonObjectSetClass(result, jsonObjectGetClass(o));
992     return result;
993 }
994
995 /**
996         @brief Return the truth or falsity of a jsonObject of type JSON_BOOL.
997         @param boolObj Pointer to the jsonObject.
998         @return 1 or 0, depending on whether the stored boolean is true or false.
999
1000         If @a boolObj is NULL, or if it points to a jsonObject not of type JSON_BOOL, the
1001         returned value is zero.
1002 */
1003 int jsonBoolIsTrue( const jsonObject* boolObj ) {
1004     if( boolObj && boolObj->type == JSON_BOOL && boolObj->value.b )
1005         return 1;
1006     return 0;
1007 }
1008
1009
1010 /**
1011         @brief Create a copy of the string stored in a jsonObject.
1012         @param o Pointer to the jsonObject whose string is to be copied.
1013         @return Pointer to a newly allocated string copied from the jsonObject.
1014
1015         If @a o is NULL, or if it points to a jsonObject not of type JSON_STRING or JSON_NUMBER,
1016         the returned value is NULL.  In the case of a JSON_NUMBER, the string created is numeric.
1017
1018         The calling code is responsible for freeing the newly created string.
1019 */
1020 char* jsonObjectToSimpleString( const jsonObject* o ) {
1021         if(!o) return NULL;
1022
1023         char* value = NULL;
1024
1025         switch( o->type ) {
1026
1027                 case JSON_NUMBER:
1028                         value = strdup( o->value.s ? o->value.s : "0" );
1029                         break;
1030
1031                 case JSON_STRING:
1032                         value = strdup(o->value.s);
1033         }
1034
1035         return value;
1036 }
1037
1038 /**
1039  Return 1 if the string is numeric, otherwise return 0.
1040  This validation follows the rules defined by the grammar at:
1041  http://www.json.org/
1042  **/
1043 /**
1044         @brief Determine whether a specified character string is a valid JSON number.
1045         @param s Pointer to the string to be examined.
1046         @return 1 if the string is numeric, or 0 if not.
1047
1048         This function defines numericity according to JSON rules; see http://json.org/.  This
1049         determination is based purely on the lexical properties of the string.  In particular
1050         there is no guarantee that the number in a numeric string is representable in C as a
1051         long long, a long double, or any other built-in type.
1052
1053         A numeric string consists of:
1054
1055         - An optional leading minus sign (but not a plus sign)
1056         - One or more decimal digits.  The first digit may be a zero only if it is the only
1057           digit to the left of the decimal.
1058         - Optionally, a decimal point followed by one or more decimal digits.
1059         - An optional exponent, consisting of:
1060                 - The letter E, in upper or lower case
1061                 - An optional plus or minus sign
1062                 - One or more decimal digits
1063
1064         See also jsonScrubNumber().
1065 */
1066 int jsonIsNumeric( const char* s ) {
1067
1068         if( !s || !*s ) return 0;
1069
1070         const char* p = s;
1071
1072         // skip leading minus sign, if present (leading plus sign not allowed)
1073
1074         if( '-' == *p )
1075                 ++p;
1076
1077         // There must be at least one digit to the left of the decimal
1078
1079         if( isdigit( (unsigned char) *p ) ) {
1080                 if( '0' == *p++ ) {
1081
1082                         // If the first digit is zero, it must be the
1083                         // only digit to the left of the decimal
1084
1085                         if( isdigit( (unsigned char) *p ) )
1086                                 return 0;
1087                 }
1088                 else {
1089
1090                         // Skip over the following digits
1091
1092                         while( isdigit( (unsigned char) *p ) ) ++p;
1093                 }
1094         }
1095         else
1096                 return 0;
1097
1098         if( !*p )
1099                 return 1;             // integer
1100
1101         if( '.' == *p ) {
1102
1103                 ++p;
1104
1105                 // If there is a decimal point, there must be
1106                 // at least one digit to the right of it
1107
1108                 if( isdigit( (unsigned char) *p ) )
1109                         ++p;
1110                 else
1111                         return 0;
1112
1113                 // skip over contiguous digits
1114
1115                 while( isdigit( (unsigned char) *p ) ) ++p;
1116         }
1117
1118         if( ! *p )
1119                 return 1;  // decimal fraction, no exponent
1120         else if( *p != 'e' && *p != 'E' )
1121                 return 0;  // extra junk, no exponent
1122         else
1123                 ++p;
1124
1125         // If we get this far, we have the beginnings of an exponent.
1126         // Skip over optional sign of exponent.
1127
1128         if( '-' == *p || '+' == *p )
1129                 ++p;
1130
1131         // There must be at least one digit in the exponent
1132         
1133         if( isdigit( (unsigned char) *p ) )
1134                 ++p;
1135         else
1136                 return 0;
1137
1138         // skip over contiguous digits
1139
1140         while( isdigit( (unsigned char) *p ) ) ++p;
1141
1142         if( *p )
1143                 return 0;  // extra junk
1144         else
1145                 return 1;  // number with exponent
1146 }
1147
1148 /**
1149         @brief Edit a string into a valid JSON number, if possible.
1150         @param s Pointer to the string to be edited.
1151         @return A pointer to a newly created numeric string, if possible; otherwise NULL.
1152
1153         JSON has rather exacting requirements about what constitutes a valid numeric string (see
1154         jsonIsNumeric()).  Real-world input may be a bit sloppy.  jsonScrubNumber accepts numeric
1155         strings in a less formal format and reformats them, where possible, according to JSON
1156         rules.  It removes leading white space, a leading plus sign, and extraneous leading zeros.
1157         It adds a leading zero as needed when the absolute value is less than 1.  It also accepts
1158         scientific notation in the form of a bare exponent (e.g. "E-3"), supplying a leading factor
1159         of "1".
1160
1161         If the input string is non-numeric even according to these relaxed rules, the return value
1162         is NULL.
1163
1164         The calling code is responsible for freeing the newly created numeric string.
1165 */
1166 char* jsonScrubNumber( const char* s ) {
1167         if( !s || !*s ) return NULL;
1168
1169         growing_buffer* buf = buffer_init( 64 );
1170
1171         // Skip leading white space, if present
1172
1173         while( isspace( (unsigned char) *s ) ) ++s;
1174
1175         // Skip leading plus sign, if present, but keep a minus
1176
1177         if( '-' == *s )
1178         {
1179                 buffer_add_char( buf, '-' );
1180                 ++s;
1181         }
1182         else if( '+' == *s )
1183                 ++s;
1184
1185         if( '\0' == *s ) {
1186                 // No digits found
1187
1188                 buffer_free( buf );
1189                 return NULL;
1190         }
1191         // Skip any leading zeros
1192
1193         while( '0' == *s ) ++s;
1194
1195         // Capture digits to the left of the decimal,
1196         // and note whether there are any.
1197
1198         int left_digit = 0;  // boolean
1199
1200         if( isdigit( (unsigned char) *s ) ) {
1201                 buffer_add_char( buf, *s++ );
1202                 left_digit = 1;
1203         }
1204         
1205         while( isdigit( (unsigned char) *s  ) )
1206                 buffer_add_char( buf, *s++ );
1207
1208         // Now we expect to see a decimal point,
1209         // an exponent, or end-of-string.
1210
1211         switch( *s )
1212         {
1213                 case '\0' :
1214                         break;
1215                 case '.' :
1216                 {
1217                         // Add a single leading zero, if we need to
1218
1219                         if( ! left_digit )
1220                                 buffer_add_char( buf, '0' );
1221                         buffer_add_char( buf, '.' );
1222                         ++s;
1223
1224                         if( ! left_digit && ! isdigit( (unsigned char) *s ) )
1225                         {
1226                                 // No digits on either side of decimal
1227
1228                                 buffer_free( buf );
1229                                 return NULL;
1230                         }
1231
1232                         // Collect digits to right of decimal
1233
1234                         while( isdigit( (unsigned char) *s ) )
1235                                 buffer_add_char( buf, *s++ );
1236
1237                         break;
1238                 }
1239                 case 'e' :
1240                 case 'E' :
1241
1242                         // Exponent; we'll deal with it later, but
1243                         // meanwhile make sure we have something
1244                         // to its left
1245
1246                         if( ! left_digit )
1247                                 buffer_add_char( buf, '1' );
1248                         break;
1249                 default :
1250
1251                         // Unexpected character; bail out
1252
1253                         buffer_free( buf );
1254                         return NULL;
1255         }
1256
1257         if( '\0' == *s )    // Are we done yet?
1258                 return buffer_release( buf );
1259
1260         if( 'e' != *s && 'E' != *s ) {
1261
1262                 // Unexpected character: bail out
1263
1264                 buffer_free( buf );
1265                 return NULL;
1266         }
1267
1268         // We have an exponent.  Load the e or E,
1269         // and the sign if there is one.
1270
1271         buffer_add_char( buf, *s++ );
1272
1273         if( '+' == *s || '-' == *s )
1274                 buffer_add_char( buf, *s++ );
1275
1276         // Collect digits of the exponent
1277
1278         while( isdigit( (unsigned char) *s ) )
1279                 buffer_add_char( buf, *s++ );
1280
1281         // There better not be anything left
1282
1283         if( *s ) {
1284                 buffer_free( buf );
1285                 return NULL;
1286         }
1287
1288         return buffer_release( buf );
1289 }