]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/objson/object.c
4f3658dd2199b6a378004e6d8dd3414f1e2b62aa
[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                 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 && 
349                         index < obj->size && obj->type == JSON_ARRAY ) {
350
351                 jsonObjectNode* node = obj->value.c;
352                 while(node) {
353                         if(node->index == index)
354                                 return node->item;
355                         node = node->next;
356                 }
357         }
358
359         return NULL;
360 }
361
362 jsonObject* jsonObjectGetKey( const jsonObject* obj, const char* key ) {
363
364         if( obj && key && obj->type == JSON_HASH ) {
365
366                 jsonObjectNode* node = obj->value.c;
367         
368                 while(node) {
369                         if(node->key && !strcmp(node->key, key))
370                                 return node->item;
371                         node = node->next;
372                 }       
373         }
374
375         return NULL;
376 }
377
378 char* jsonObjectGetString( const jsonObject* obj ) {
379         if( obj && obj->type == JSON_STRING ) return obj->value.s;
380         return NULL;
381 }
382
383 double jsonObjectGetNumber( const jsonObject* obj ) {
384         if( obj && obj->type == JSON_NUMBER ) return obj->value.n;
385         return 0;
386 }
387
388 void jsonObjectSetString( jsonObject* obj, const char* string) {
389         if( obj ) {
390                 obj->type = JSON_STRING;
391                 if(string) obj->value.s = strdup(string);
392                 else obj->value.s = NULL; 
393         }
394 }
395
396
397 void jsonObjectSetNumber( jsonObject* obj, double num) {
398         if(obj) {
399                 obj->type = JSON_NUMBER;
400                 obj->value.n = num;
401         }
402 }
403
404
405 void jsonObjectSetClass( jsonObject* obj, const char* classname) {
406         if( obj == NULL || classname == NULL ) return;
407         obj->classname = strdup(classname);
408 }
409
410
411
412 char* jsonObjectToJSON( const jsonObject* obj ) {
413
414         if(obj == NULL) return strdup("null");
415
416         growing_buffer* buf = buffer_init(64);
417
418         /* add class hints if we have a class name */
419         if(obj->classname) {
420                 buffer_add(buf,"/*--S ");
421                 buffer_add(buf,obj->classname);
422                 buffer_add(buf, "--*/");
423         }
424
425         switch( obj->type ) {
426
427                 case JSON_BOOL: 
428                         if(obj->value.b) buffer_add(buf, "true"); 
429                         else buffer_add(buf, "false"); 
430                         break;
431
432                 case JSON_NUMBER: {
433                         double x = obj->value.n;
434
435                         /* if the number does not need to be a double,
436                                 turn it into an int on the way out */
437                         if( x == (int) x ) {
438                                 INT_TO_STRING((int)x);  
439                                 buffer_add(buf, INTSTR);
440
441                         } else {
442                                 DOUBLE_TO_STRING(x);
443                                 buffer_add(buf, DOUBLESTR);
444                         }
445                         break;
446                 }
447
448                 case JSON_NULL:
449                         buffer_add(buf, "null");
450                         break;
451
452                 case JSON_STRING:
453                         buffer_add(buf, "\"");
454                         char* data = obj->value.s;
455                         int len = strlen(data);
456                         
457                         char* output = uescape(data, len, 1);
458                         buffer_add(buf, output);
459                         free(output);
460                         buffer_add(buf, "\"");
461                         break;
462
463                 case JSON_ARRAY:
464                         buffer_add(buf, "[");
465                         int i;
466                         for( i = 0; i!= obj->size; i++ ) {
467                                 const jsonObject* x = jsonObjectGetIndex(obj,i);
468                                 char* data = jsonObjectToJSON(x);
469         
470 #ifdef STRICT_JSON_WRITE
471                                 buffer_add(buf, data);
472 #else
473                                 if(strcmp(data,"null")) /* only add the string if it isn't null */
474                                         buffer_add(buf, data);
475 #endif
476         
477                                 free(data);
478                                 if(i != obj->size - 1)
479                                         buffer_add(buf, ",");
480                         }
481                         buffer_add(buf, "]");
482                         break;  
483
484                 case JSON_HASH:
485         
486                         buffer_add(buf, "{");
487                         jsonObjectIterator* itr = jsonNewObjectIterator(obj);
488                         jsonObjectNode* tmp;
489         
490                         while( (tmp = jsonObjectIteratorNext(itr)) ) {
491
492                                 buffer_add(buf, "\"");
493                                 buffer_add(buf, tmp->key);
494                                 buffer_add(buf, "\":");
495                                 char* data =  jsonObjectToJSON(tmp->item);
496
497 #ifdef STRICT_JSON_WRITE
498                                 buffer_add(buf, data);
499 #else
500                                 if(strcmp(data,"null")) /* only add the string if it isn't null */
501                                         buffer_add(buf, data);
502 #endif
503
504                                 if(jsonObjectIteratorHasNext(itr))
505                                         buffer_add(buf, ",");
506                                 free(data);
507                         }
508
509                         jsonObjectIteratorFree(itr);
510                         buffer_add(buf, "}");
511                         break;
512                 
513                         default:
514                                 fprintf(stderr, "Unknown object type %d\n", obj->type);
515                                 break;
516                                 
517         }
518
519         /* close out the object hint */
520         if(obj->classname) {
521                 buffer_add(buf, "/*--E ");
522                 buffer_add(buf, obj->classname);
523                 buffer_add(buf, "--*/");
524         }
525
526         if(obj->comment) {
527                 buffer_add(buf, " /*");
528                 buffer_add(buf, obj->comment);
529                 buffer_add(buf, "*/");
530         }
531
532         char* data = buffer_data(buf);
533         buffer_free(buf);
534         return data;
535
536 }
537
538
539 void jsonObjectSetComment( jsonObject* obj, const char* com) {
540         if( obj == NULL || com == NULL ) return;
541         obj->comment = strdup(com);
542 }
543
544
545 char* __tabs(int count) {
546         growing_buffer* buf = buffer_init(24);
547         int i;
548         for(i=0;i!=count;i++) buffer_add(buf, "   ");
549         char* final = buffer_data( buf );
550         buffer_free( buf );
551         return final;
552 }
553
554 char* jsonFormatString( const char* string ) {
555
556         if(!string) return strdup("");
557
558         growing_buffer* buf = buffer_init(64);
559         int i;
560         int depth = 0;
561         char* tab = NULL;
562
563         for(i=0; i!= strlen(string); i++) {
564
565                 if( string[i] == '{' || string[i] == '[' ) {
566
567                         tab = __tabs(++depth);
568                         buffer_fadd( buf, "%c\n%s", string[i], tab);
569                         free(tab);
570
571                 } else if( string[i] == '}' || string[i] == ']' ) {
572
573                         tab = __tabs(--depth);
574                         buffer_fadd( buf, "\n%s%c", tab, string[i]);
575                         free(tab);
576
577                         if(string[i+1] != ',') {
578                                 tab = __tabs(depth);
579                                 buffer_fadd( buf, "\n%s", tab );        
580                                 free(tab);
581                         }
582
583                 } else if( string[i] == ',' ) {
584
585                         tab = __tabs(depth);
586                         buffer_fadd(buf, ",\n%s", tab);
587                         free(tab);
588
589                 } else { buffer_add_char(buf, string[i]); }
590
591         }
592
593         char* result = buffer_data(buf);
594         buffer_free(buf);
595         return result;
596
597 }
598
599
600 jsonObject* jsonObjectClone(const jsonObject* o) {
601         if(!o) return NULL;
602         char* json = jsonObjectToJSON(o);
603         jsonObject* newo = jsonParseString(json);
604         free(json);
605         return newo;
606 }
607
608
609
610 /* ---------------------------------------------------------------------- */
611 /* Iterator */
612
613 jsonObjectIterator* jsonNewObjectIterator(const jsonObject* obj) {
614
615         if(!obj) return NULL;
616         jsonObjectIterator* iter = safe_malloc(sizeof(jsonObjectIterator));
617         iter->obj = obj;
618
619         if( obj->type ==  JSON_HASH || obj->type == JSON_ARRAY ) 
620                 iter->current = obj->value.c;
621         else iter->current = NULL;
622         return iter;
623 }
624
625 jsonObjectNode* jsonObjectIteratorNext( jsonObjectIterator* itr ) {
626         if( itr == NULL ) return NULL;
627
628         jsonObjectNode* tmp = itr->current;
629         if(tmp == NULL) return NULL;
630         itr->current = itr->current->next;
631
632         return tmp;
633 }
634
635 void jsonObjectIteratorFree(jsonObjectIterator* iter) { 
636         free(iter);
637 }
638
639 int jsonObjectIteratorHasNext(const jsonObjectIterator* itr) {
640         return (itr && itr->current);
641 }
642
643
644 jsonObject* jsonObjectFindPath( const jsonObject* obj, char* format, ...) {
645         if(!obj || !format || strlen(format) < 1) return NULL;  
646
647         VA_LIST_TO_STRING(format);
648         char* buf = VA_BUF;
649
650         /* tmp storage for strtok_r */
651         //char tokbuf[len];             
652         //bzero(tokbuf, len);
653
654         char* token = NULL;
655         char* t = buf;
656         //char* tt = tokbuf;
657         char* tt; /* strtok storage */
658
659         /* copy the path before strtok_r destroys it */
660         char* pathcopy = strdup(buf);
661
662         /* grab the root of the path */
663         token = strtok_r(t, "/", &tt);
664         if(!token) return NULL;
665
666         /* special case where path starts with //  (start anywhere) */
667         if(strlen(pathcopy) > 2 && pathcopy[0] == '/' && pathcopy[1] == '/') {
668                 jsonObject* it = _jsonObjectFindPathRecurse(obj, token, pathcopy + 1);
669                 free(pathcopy);
670                 return it;
671         }
672
673         free(pathcopy);
674
675         t = NULL;
676         do { 
677                 obj = jsonObjectGetKey(obj, token);
678         } while( (token = strtok_r(NULL, "/", &tt)) && obj);
679
680         return jsonObjectClone(obj);
681 }
682
683 /* --------------------------------------------------------------- */
684
685
686
687 jsonObject* _jsonObjectFindPathRecurse(const jsonObject* obj, char* root, char* path) {
688
689         if(!obj || ! root || !path) return NULL;
690
691         /* collect all of the potential objects */
692         jsonObject* arr = __jsonObjectFindPathRecurse(obj, root);
693
694         /* container for fully matching objects */
695         jsonObject* newarr = jsonParseString("[]");
696         int i;
697
698         /* path is just /root or /root/ */
699         if( strlen(root) + 2 >= strlen(path) ) {
700                 return arr;
701
702         } else {
703
704                 /* gather all of the sub-objects that match the full path */
705                 for( i = 0; i < arr->size; i++ ) {
706                         jsonObject* a = jsonObjectGetIndex(arr, i);
707                         jsonObject* thing = jsonObjectFindPath(a , path + strlen(root) + 1); 
708
709                         if(thing) { //jsonObjectPush(newarr, thing);
710                 if(thing->type == JSON_ARRAY) {
711                 int i;
712                                         for( i = 0; i != thing->size; i++ )
713                                                 jsonObjectPush(newarr, jsonObjectClone(jsonObjectGetIndex(thing,i)));
714                                         jsonObjectFree(thing);
715
716                                 } else {
717                                         jsonObjectPush(newarr, thing);
718                                 }                                               
719                         }
720                 }
721         }
722         
723         jsonObjectFree(arr);
724         return newarr;
725 }
726
727 jsonObject* __jsonObjectFindPathRecurse(const jsonObject* obj, char* root) {
728
729         jsonObject* arr = jsonParseString("[]");
730         if(!obj) return arr;
731
732         int i;
733
734         /* if the current object has a node that matches, add it */
735
736         jsonObject* o = jsonObjectGetKey(obj, root);
737         if(o) jsonObjectPush( arr, jsonObjectClone(o) );
738
739         jsonObjectNode* tmp = NULL;
740         jsonObject* childarr;
741         jsonObjectIterator* itr = jsonNewObjectIterator(obj);
742
743         /* recurse through the children and find all potential nodes */
744         while( (tmp = jsonObjectIteratorNext(itr)) ) {
745                 childarr = __jsonObjectFindPathRecurse(tmp->item, root);
746                 if(childarr && childarr->size > 0) {
747                         for( i = 0; i!= childarr->size; i++ ) {
748                                 jsonObjectPush( arr, jsonObjectClone(jsonObjectGetIndex(childarr, i)) );
749                         }
750                 }
751                 jsonObjectFree(childarr);
752         }
753
754         jsonObjectIteratorFree(itr);
755
756         return arr;
757 }
758
759
760 char* jsonObjectToSimpleString( const jsonObject* o ) {
761         char* value = NULL;
762
763         if(o) {
764                 switch( o->type ) {
765
766                         case JSON_NUMBER: {
767
768                                 if( o->value.n == (int) o->value.n ) {
769                                         INT_TO_STRING((int) o->value.n);        
770                                         value = strdup(INTSTR);
771         
772                                 } else {
773                                         DOUBLE_TO_STRING(o->value.n);
774                                         value = strdup(DOUBLESTR);
775                                 }
776
777                                 break;
778                         }
779
780                         case JSON_STRING:
781                                 value = strdup(o->value.s);
782                 }
783         }       
784         return value;
785 }
786
787