]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/objson/object.c
new json api changes
[OpenSRF.git] / src / objson / object.c
1 /*
2 Copyright (C) 2005  Georgia Public Library Service 
3 Bill Erickson <highfalutin@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 #include "object.h"
17 #include "json_parser.h"
18
19
20 /* ---------------------------------------------------------------------- */
21 /* See object.h for function info */
22 /* ---------------------------------------------------------------------- */
23
24
25 char* __tabs(int count);
26
27 jsonObject* jsonNewObject( const char* stringValue ) { 
28
29         jsonObject* obj = (jsonObject*) safe_malloc(sizeof(jsonObject));
30         obj->size                       = 0;
31         obj->type = JSON_NULL;
32
33         if(stringValue) {
34                 obj->type = JSON_STRING;
35                 obj->value.s = strdup(stringValue);
36         }
37
38         return obj;
39 }
40
41
42
43
44 jsonObject* jsonNewNumberObject( double num ) {
45         jsonObject* o = jsonNewObject(NULL);
46         o->type = JSON_NUMBER;
47         o->value.n = num;
48         return o;
49
50 }
51
52
53 jsonObjectNode* jsonNewObjectNode( jsonObject* obj ) {
54         jsonObjectNode* node = (jsonObjectNode*) safe_malloc(sizeof(jsonObjectNode));
55         node->item = obj;
56         node->next = NULL;
57         node->index = -1;
58         return node;
59 }
60
61 unsigned long jsonObjectPush( jsonObject* obj, jsonObject* new_obj) {
62         if(!obj) return -1;
63
64         obj->type = JSON_ARRAY;
65
66         if(new_obj == NULL) {
67                 new_obj = jsonNewObject(NULL);
68                 new_obj->type = JSON_NULL;
69         }
70
71         jsonObjectNode* node = jsonNewObjectNode(new_obj);
72         node->index = obj->size++;
73
74         if(obj->value.c == NULL) {
75                 obj->value.c = node;
76
77         } else {
78                 /* append the node onto the end */
79                 jsonObjectNode* tmp = obj->value.c;
80                 while(tmp) {
81                         if(tmp->next == NULL) break;
82                         tmp = tmp->next;
83                 }
84                 tmp->next = node;
85         }
86         return obj->size;
87 }
88
89 unsigned long  jsonObjectSetIndex( jsonObject* obj, unsigned long index, jsonObject* new_obj) {
90         if( obj == NULL ) return -1;
91         obj->type = JSON_ARRAY;
92
93         if(obj->size <= index)
94                 obj->size = index + 1;
95
96         if(new_obj == NULL) {
97                 new_obj = jsonNewObject(NULL);
98                 new_obj->type = JSON_NULL;
99         }
100
101         jsonObjectNode* node = jsonNewObjectNode(new_obj);
102         node->index = index;
103         
104         if( obj->value.c == NULL ) {
105                 obj->value.c = node;
106
107         } else {
108
109                 if(obj->value.c->index == index) {
110                         jsonObjectNode* tmp = obj->value.c->next;
111                         jsonObjectNodeFree(obj->value.c);
112                         obj->value.c = node;
113                         node->next = tmp;
114
115                 } else {
116                 
117                         jsonObjectNode* prev = obj->value.c;
118                         jsonObjectNode* cur = prev->next;
119                         int inserted = 0;
120
121                         while(cur != NULL) {
122
123                                 /* replace an existing node */
124                                 if( cur->index == index ) {
125                                         jsonObjectNode* tmp = cur->next;
126                                         jsonObjectNodeFree(cur);
127                                         node->next = tmp;
128                                         prev->next = node;
129                                         inserted = 1;
130                                         break;
131                                         
132                                         /* instert between two nodes */
133                                 } else if( prev->index < index && cur->index > index ) {
134                                         prev->next = node;
135                                         node->next = cur;
136                                         inserted = 1;
137                                         break;
138                                 }
139                                 prev = cur;
140                                 cur = cur->next;
141                         }
142
143                         /* shove on to the end */
144                         if(!inserted) 
145                                 prev->next = node;
146                 }
147         }
148
149         return obj->size;
150 }
151
152
153 void _jsonObjectShifIndex( jsonObject* obj, unsigned long index) {
154         if( obj == NULL || index < 0 ) return;
155
156         if(obj->value.c == NULL) {
157                 obj->size = 0;
158                 return;
159         }
160
161         jsonObjectNode* data = obj->value.c;
162         while(data) {
163                 if(data->index >= index)
164                         data->index--;
165                 data = data->next;
166         }
167         obj->size--;
168 }
169
170 unsigned long jsonObjectRemoveIndex( jsonObject* obj, unsigned long index) {
171         if( obj == NULL || index < 0 ) return -1;
172
173         if(obj->value.c == NULL) return 0;
174
175         /* removing the first item in the list */
176         if(obj->value.c->index == index) {
177                 jsonObjectNode* tmp = obj->value.c->next;
178                 jsonObjectNodeFree(obj->value.c);
179                 obj->value.c = tmp;
180                 _jsonObjectShiftIndex(obj,index);
181                 return obj->size;
182         }
183
184
185         jsonObjectNode* prev = obj->value.c;
186         jsonObjectNode* cur = prev->next;
187
188         while(cur) {
189                 if(cur->index == index) {
190                         jsonObjectNode* tmp = cur->next;
191                         jsonObjectNodeFree(cur);
192                         prev->next = tmp;
193                         _jsonObjectShiftIndex(obj, index);
194                         break;
195                 }
196                 prev = cur;
197                 cur = cur->next;
198         }
199
200         return obj->size;       
201 }
202
203
204 void _jsonObjectShiftIndex(jsonObject* obj, unsigned long index) {
205
206         if( ! obj ) return;
207
208         if(obj->value.c == NULL) {
209                 obj->size = 0;
210                 return;
211         }
212
213         jsonObjectNode* data = obj->value.c;
214         while(data) {
215                 if(data->index >= index)
216                         data->index--;
217                 data = data->next;
218         }
219         obj->size--;
220 }
221
222
223 unsigned long jsonObjectRemoveKey( jsonObject* obj, const char* key) {
224         if( obj == NULL || key == NULL ) return -1;
225
226         if(obj->value.c == NULL) return 0;
227
228         /* removing the first item in the list */
229         if(!strcmp(obj->value.c->key, key)) {
230
231                 jsonObjectNode* tmp = obj->value.c->next;
232                 jsonObjectNodeFree(obj->value.c);
233
234                 obj->value.c = tmp;
235                 if(!obj->value.c) obj->size = 0;
236                 return obj->size;
237         }
238
239         jsonObjectNode* prev = obj->value.c;
240         jsonObjectNode* cur = prev->next;
241
242         while(cur) {
243                 if(!strcmp(cur->key,key)) {
244
245                         jsonObjectNode* tmp = cur->next;
246                         jsonObjectNodeFree(cur);
247                         prev->next = tmp;
248                         obj->size--;
249                         break;
250                 }
251                 prev = cur;
252                 cur = cur->next;
253         }
254
255         return obj->size;
256 }
257
258
259 unsigned long jsonObjectSetKey( jsonObject* obj, const char* key, jsonObject* new_obj ) {
260         if( obj == NULL || key == NULL ) return -1;
261         obj->type = JSON_HASH;
262
263         if(new_obj == NULL) {
264                 new_obj = jsonNewObject(NULL);
265                 new_obj->type = JSON_NULL;
266         }
267
268         jsonObjectNode* node = jsonNewObjectNode(new_obj);
269         node->key = strdup(key);
270         
271         if( obj->value.c == NULL ) {
272                 obj->value.c = node;
273                 obj->size++;
274
275         } else {
276
277                 /* replace the first node */
278                 if(!strcmp(obj->value.c->key, key)) {
279                         jsonObjectNode* tmp = obj->value.c->next;
280                         jsonObjectNodeFree(obj->value.c);
281                         obj->value.c = node;
282                         node->next = tmp;
283
284                 } else {
285                 
286                         jsonObjectNode* prev = obj->value.c;
287                         jsonObjectNode* cur = prev->next;
288                         int inserted = 0;
289
290                         while(cur != NULL) {
291
292                                 /* replace an existing node */
293                                 if( !strcmp(cur->key, key) ) {
294                                         jsonObjectNode* tmp = cur->next;
295                                         jsonObjectNodeFree(cur);
296                                         node->next = tmp;
297                                         prev->next = node;
298                                         inserted = 1;
299                                         break;
300                                 }
301                                         
302                                 prev = cur;
303                                 cur = cur->next;
304                         }
305
306                         /* shove on to the end */
307                         if(!inserted) {
308                                 prev->next = node;
309                                 obj->size++;
310                         }
311                 }
312         }
313
314         return obj->size;
315 }
316
317
318 void jsonObjectFree( jsonObject* obj) {
319         if(obj == NULL) return;
320
321         free(obj->classname);
322         free(obj->comment);
323
324         if( obj->type == JSON_ARRAY || obj->type == JSON_HASH ) {
325                 while(obj->value.c) {
326                         jsonObjectNode* tmp = obj->value.c->next;
327                         jsonObjectNodeFree(obj->value.c);
328                         obj->value.c = tmp;
329                 }
330         }
331
332         if(obj->type == JSON_STRING)
333                 free(obj->value.s);
334
335         free(obj);
336 }
337
338 void jsonObjectNodeFree( jsonObjectNode* node ) {
339         if(node == NULL) return;
340         free(node->key);
341         jsonObjectFree(node->item);
342         free(node);
343 }
344
345 jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index ) {
346
347         if( obj && index >= 0 && obj->type == JSON_ARRAY ) {
348
349                 jsonObjectNode* node = obj->value.c;
350                 while(node) {
351                         if(node->index == index)
352                                 return node->item;
353                         node = node->next;
354                 }
355         }
356
357         return NULL;
358 }
359
360 jsonObject* jsonObjectGetKey( const jsonObject* obj, const char* key ) {
361
362         if( obj && key && obj->type == JSON_HASH ) {
363
364                 jsonObjectNode* node = obj->value.c;
365         
366                 while(node) {
367                         if(node->key && !strcmp(node->key, key))
368                                 return node->item;
369                         node = node->next;
370                 }       
371         }
372
373         return NULL;
374 }
375
376 char* jsonObjectGetString( const jsonObject* obj ) {
377         if( obj && obj->type == JSON_STRING ) return obj->value.s;
378         return NULL;
379 }
380
381 double jsonObjectGetNumber( const jsonObject* obj ) {
382         if( obj && obj->type == JSON_NUMBER ) return obj->value.n;
383         return 0;
384 }
385
386 void jsonObjectSetString( jsonObject* obj, const char* string) {
387         if( obj ) {
388                 obj->type = JSON_STRING;
389                 if(string) obj->value.s = strdup(string);
390                 else obj->value.s = NULL; 
391         }
392 }
393
394
395 void jsonObjectSetNumber( jsonObject* obj, double num) {
396         if(obj) {
397                 obj->type = JSON_NUMBER;
398                 obj->value.n = num;
399         }
400 }
401
402
403 void jsonObjectSetClass( jsonObject* obj, const char* classname) {
404         if( obj == NULL || classname == NULL ) return;
405         obj->classname = strdup(classname);
406 }
407
408
409
410 char* jsonObjectToJSON( const jsonObject* obj ) {
411
412         if(obj == NULL) return strdup("null");
413
414         growing_buffer* buf = buffer_init(64);
415
416         /* add class hints if we have a class name */
417         if(obj->classname) {
418                 buffer_add(buf,"/*--S ");
419                 buffer_add(buf,obj->classname);
420                 buffer_add(buf, "--*/");
421         }
422
423         switch( obj->type ) {
424
425                 case JSON_BOOL: 
426                         if(obj->value.b) buffer_add(buf, "true"); 
427                         else buffer_add(buf, "false"); 
428                         break;
429
430                 case JSON_NUMBER: {
431                         double x = obj->value.n;
432
433                         /* if the number does not need to be a double,
434                                 turn it into an int on the way out */
435                         if( x == (int) x ) {
436                                 INT_TO_STRING((int)x);  
437                                 buffer_add(buf, INTSTR);
438
439                         } else {
440                                 DOUBLE_TO_STRING(x);
441                                 buffer_add(buf, DOUBLESTR);
442                         }
443                         break;
444                 }
445
446                 case JSON_NULL:
447                         buffer_add(buf, "null");
448                         break;
449
450                 case JSON_STRING:
451                         buffer_add(buf, "\"");
452                         char* data = obj->value.s;
453                         int len = strlen(data);
454                         
455                         char* output = uescape(data, len, 1);
456                         buffer_add(buf, output);
457                         free(output);
458                         buffer_add(buf, "\"");
459                         break;
460
461                 case JSON_ARRAY:
462                         buffer_add(buf, "[");
463                         int i;
464                         for( i = 0; i!= obj->size; i++ ) {
465                                 const jsonObject* x = jsonObjectGetIndex(obj,i);
466                                 char* data = jsonObjectToJSON(x);
467         
468 #ifdef STRICT_JSON_WRITE
469                                 buffer_add(buf, data);
470 #else
471                                 if(strcmp(data,"null")) /* only add the string if it isn't null */
472                                         buffer_add(buf, data);
473 #endif
474         
475                                 free(data);
476                                 if(i != obj->size - 1)
477                                         buffer_add(buf, ",");
478                         }
479                         buffer_add(buf, "]");
480                         break;  
481
482                 case JSON_HASH:
483         
484                         buffer_add(buf, "{");
485                         jsonObjectIterator* itr = jsonNewObjectIterator(obj);
486                         jsonObjectNode* tmp;
487         
488                         while( (tmp = jsonObjectIteratorNext(itr)) ) {
489
490                                 buffer_add(buf, "\"");
491                                 buffer_add(buf, tmp->key);
492                                 buffer_add(buf, "\":");
493                                 char* data =  jsonObjectToJSON(tmp->item);
494
495 #ifdef STRICT_JSON_WRITE
496                                 buffer_add(buf, data);
497 #else
498                                 if(strcmp(data,"null")) /* only add the string if it isn't null */
499                                         buffer_add(buf, data);
500 #endif
501
502                                 if(jsonObjectIteratorHasNext(itr))
503                                         buffer_add(buf, ",");
504                                 free(data);
505                         }
506
507                         jsonObjectIteratorFree(itr);
508                         buffer_add(buf, "}");
509                         break;
510                 
511                         default:
512                                 fprintf(stderr, "Unknown object type %d\n", obj->type);
513                                 break;
514                                 
515         }
516
517         /* close out the object hint */
518         if(obj->classname) {
519                 buffer_add(buf, "/*--E ");
520                 buffer_add(buf, obj->classname);
521                 buffer_add(buf, "--*/");
522         }
523
524         if(obj->comment) {
525                 buffer_add(buf, " /*");
526                 buffer_add(buf, obj->comment);
527                 buffer_add(buf, "*/");
528         }
529
530         char* data = buffer_data(buf);
531         buffer_free(buf);
532         return data;
533
534 }
535
536
537 void jsonObjectSetComment( jsonObject* obj, const char* com) {
538         if( obj == NULL || com == NULL ) return;
539         obj->comment = strdup(com);
540 }
541
542
543 char* __tabs(int count) {
544         growing_buffer* buf = buffer_init(24);
545         int i;
546         for(i=0;i!=count;i++) buffer_add(buf, "   ");
547         char* final = buffer_data( buf );
548         buffer_free( buf );
549         return final;
550 }
551
552 char* jsonFormatString( const char* string ) {
553
554         if(!string) return strdup("");
555
556         growing_buffer* buf = buffer_init(64);
557         int i;
558         int depth = 0;
559         char* tab = NULL;
560
561         for(i=0; i!= strlen(string); i++) {
562
563                 if( string[i] == '{' || string[i] == '[' ) {
564
565                         tab = __tabs(++depth);
566                         buffer_fadd( buf, "%c\n%s", string[i], tab);
567                         free(tab);
568
569                 } else if( string[i] == '}' || string[i] == ']' ) {
570
571                         tab = __tabs(--depth);
572                         buffer_fadd( buf, "\n%s%c", tab, string[i]);
573                         free(tab);
574
575                         if(string[i+1] != ',') {
576                                 tab = __tabs(depth);
577                                 buffer_fadd( buf, "\n%s", tab );        
578                                 free(tab);
579                         }
580
581                 } else if( string[i] == ',' ) {
582
583                         tab = __tabs(depth);
584                         buffer_fadd(buf, ",\n%s", tab);
585                         free(tab);
586
587                 } else { buffer_add_char(buf, string[i]); }
588
589         }
590
591         char* result = buffer_data(buf);
592         buffer_free(buf);
593         return result;
594
595 }
596
597
598 jsonObject* jsonObjectClone(const jsonObject* o) {
599         if(!o) return NULL;
600         char* json = jsonObjectToJSON(o);
601         jsonObject* newo = jsonParseString(json);
602         free(json);
603         return newo;
604 }
605
606
607
608 /* ---------------------------------------------------------------------- */
609 /* Iterator */
610
611 jsonObjectIterator* jsonNewObjectIterator(const jsonObject* obj) {
612
613         if(!obj) return NULL;
614         jsonObjectIterator* iter = safe_malloc(sizeof(jsonObjectIterator));
615         iter->obj = obj;
616
617         if( obj->type ==  JSON_HASH || obj->type == JSON_ARRAY ) 
618                 iter->current = obj->value.c;
619         else iter->current = NULL;
620         return iter;
621 }
622
623 jsonObjectNode* jsonObjectIteratorNext( jsonObjectIterator* itr ) {
624         if( itr == NULL ) return NULL;
625
626         jsonObjectNode* tmp = itr->current;
627         if(tmp == NULL) return NULL;
628         itr->current = itr->current->next;
629
630         return tmp;
631 }
632
633 void jsonObjectIteratorFree(jsonObjectIterator* iter) { 
634         free(iter);
635 }
636
637 int jsonObjectIteratorHasNext(const jsonObjectIterator* itr) {
638         return (itr && itr->current);
639 }
640
641
642 jsonObject* jsonObjectFindPath( const jsonObject* obj, char* format, ...) {
643         if(!obj || !format || strlen(format) < 1) return NULL;  
644
645         VA_LIST_TO_STRING(format);
646         char* buf = VA_BUF;
647
648         /* tmp storage for strtok_r */
649         char tokbuf[len];               
650         bzero(tokbuf, len);
651
652         char* token = NULL;
653         char* t = buf;
654         char* tt = tokbuf;
655
656         /* copy the path before strtok_r destroys it */
657         char* pathcopy = strdup(buf);
658
659         /* grab the root of the path */
660         token = strtok_r(t, "/", &tt);
661         if(!token) return NULL;
662
663         /* special case where path starts with //  (start anywhere) */
664         if(strlen(pathcopy) > 2 && pathcopy[0] == '/' && pathcopy[1] == '/') {
665                 jsonObject* it = _jsonObjectFindPathRecurse(obj, token, pathcopy + 1);
666                 free(pathcopy);
667                 return it;
668         }
669
670         free(pathcopy);
671
672         t = NULL;
673         do { 
674                 obj = jsonObjectGetKey(obj, token);
675         } while( (token = strtok_r(NULL, "/", &tt)) && obj);
676
677         return jsonObjectClone(obj);
678 }
679
680 /* --------------------------------------------------------------- */
681
682
683
684 jsonObject* _jsonObjectFindPathRecurse(const jsonObject* obj, char* root, char* path) {
685
686         if(!obj || ! root || !path) return NULL;
687
688         /* collect all of the potential objects */
689         jsonObject* arr = __jsonObjectFindPathRecurse(obj, root);
690
691         /* container for fully matching objects */
692         jsonObject* newarr = jsonParseString("[]");
693         int i;
694
695         /* path is just /root or /root/ */
696         if( strlen(root) + 2 >= strlen(path) ) {
697                 return arr;
698
699         } else {
700
701                 /* gather all of the sub-objects that match the full path */
702                 for( i = 0; i < arr->size; i++ ) {
703                         jsonObject* a = jsonObjectGetIndex(arr, i);
704                         jsonObject* thing = jsonObjectFindPath(a , path + strlen(root) + 1); 
705                         if(thing) jsonObjectPush(newarr, thing);
706                 }
707         }
708         
709         jsonObjectFree(arr);
710         return newarr;
711 }
712
713 jsonObject* __jsonObjectFindPathRecurse(const jsonObject* obj, char* root) {
714
715         jsonObject* arr = jsonParseString("[]");
716         if(!obj) return arr;
717
718         int i;
719
720         /* if the current object has a node that matches, add it */
721
722         jsonObject* o = jsonObjectGetKey(obj, root);
723         if(o) jsonObjectPush( arr, jsonObjectClone(o) );
724
725         jsonObjectNode* tmp = NULL;
726         jsonObject* childarr;
727         jsonObjectIterator* itr = jsonNewObjectIterator(obj);
728
729         /* recurse through the children and find all potential nodes */
730         while( (tmp = jsonObjectIteratorNext(itr)) ) {
731                 childarr = __jsonObjectFindPathRecurse(tmp->item, root);
732                 if(childarr && childarr->size > 0) {
733                         for( i = 0; i!= childarr->size; i++ ) {
734                                 jsonObjectPush( arr, jsonObjectClone(jsonObjectGetIndex(childarr, i)) );
735                         }
736                 }
737                 jsonObjectFree(childarr);
738         }
739
740         jsonObjectIteratorFree(itr);
741
742         return arr;
743 }
744
745
746 char* jsonObjectToSimpleString( const jsonObject* o ) {
747         char* value = NULL;
748
749         if(o) {
750                 switch( o->type ) {
751
752                         case JSON_NUMBER: {
753                                 DOUBLE_TO_STRING(o->value.n);
754                                 value = strdup(DOUBLESTR);
755                                 break;
756                         }
757
758                         case JSON_STRING:
759                                 value = strdup(o->value.s);
760                 }
761         }       
762         return value;
763 }
764
765