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