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