]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/objson/object.c
made json formatter a little smarter/better
[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 #include <fcntl.h>
19
20
21
22 /* ---------------------------------------------------------------------- */
23 /* See object.h for function info */
24 /* ---------------------------------------------------------------------- */
25
26
27 char* __tabs(int count);
28
29 object* new_object(char* string_value) {
30         return _init_object(string_value);
31 }
32
33
34 object* new_int_object(long num) {
35         object* o = new_object(NULL);
36         o->is_null = 0;
37         o->is_number = 1;
38         o->num_value = num;
39         return o;
40 }
41
42 object* new_double_object(double num) {
43         object* o = new_object(NULL);
44         o->is_null = 0;
45         o->is_double = 1;
46         o->double_value = num;
47         return o;
48 }
49
50 object* _init_object(char* string_value) {
51
52         object* obj                     = (object*) safe_malloc(sizeof(object));
53         obj->size                       = 0;
54         obj->data                       = NULL;
55
56         obj->push                       = &object_push;
57         obj->set_index          = &object_set_index;
58         obj->add_key            = &object_add_key;
59         obj->get_index          = &object_get_index;
60         obj->get_key            = &object_get_key;
61         obj->get_string = &object_get_string;
62         obj->set_string = &object_set_string;
63         obj->set_number = &object_set_number;
64         obj->set_class          = &object_set_class;
65         obj->set_double = &object_set_double;
66         obj->remove_index = &object_remove_index;
67         obj->remove_key = &object_remove_key;
68         obj->to_json            = &object_to_json;
69         obj->set_comment        = &object_set_comment;
70
71         if(string_value) {
72                 obj->is_string = 1;
73                 obj->string_data = strdup(string_value);
74         } else
75                 obj->is_null = 1;
76
77         return obj;
78 }
79
80 object_node* new_object_node(object* obj) {
81         object_node* node = (object_node*) safe_malloc(sizeof(object_node));
82         node->item = obj;
83         node->next = NULL;
84         node->index = -1;
85         return node;
86 }
87
88 unsigned long object_push(object* obj, object* new_obj) {
89         assert(obj != NULL);
90         object_clear_type(obj);
91         obj->is_array = 1;
92
93         if(new_obj == NULL) {
94                 new_obj = new_object(NULL);
95                 new_obj->is_null = 1;
96         }
97
98         object_node* node = new_object_node(new_obj);
99         node->index = obj->size++;
100
101         if( obj->size > MAX_OBJECT_NODES )
102                 return -1;
103
104         if(obj->data == NULL) {
105                 obj->data = node;
106
107         } else {
108                 /* append the node onto the end */
109                 object_node* tmp = obj->data;
110                 while(tmp) {
111                         if(tmp->next == NULL) break;
112                         tmp = tmp->next;
113                 }
114                 tmp->next = node;
115         }
116         return obj->size;
117 }
118
119 unsigned long  object_set_index(object* obj, unsigned long index, object* new_obj) {
120         assert(obj != NULL && index <= MAX_OBJECT_NODES);
121         object_clear_type(obj);
122         obj->is_array = 1;
123
124         if(obj->size <= index)
125                 obj->size = index + 1;
126
127         if(new_obj == NULL) {
128                 new_obj = new_object(NULL);
129                 new_obj->is_null = 1;
130         }
131
132         object_node* node = new_object_node(new_obj);
133         node->index = index;
134         
135         if( obj->data == NULL ) {
136                 obj->data = node;
137
138         } else {
139
140                 if(obj->data->index == index) {
141                         object_node* tmp = obj->data->next;
142                         free_object_node(obj->data);
143                         obj->data = node;
144                         node->next = tmp;
145
146                 } else {
147                 
148                         object_node* prev = obj->data;
149                         object_node* cur = prev->next;
150                         int inserted = 0;
151
152                         while(cur != NULL) {
153
154                                 /* replace an existing node */
155                                 if( cur->index == index ) {
156                                         object_node* tmp = cur->next;
157                                         free_object_node(cur);
158                                         node->next = tmp;
159                                         prev->next = node;
160                                         inserted = 1;
161                                         break;
162                                         
163                                         /* instert between two nodes */
164                                 } else if( prev->index < index && cur->index > index ) {
165                                         prev->next = node;
166                                         node->next = cur;
167                                         inserted = 1;
168                                         break;
169                                 }
170                                 prev = cur;
171                                 cur = cur->next;
172                         }
173
174                         /* shove on to the end */
175                         if(!inserted) 
176                                 prev->next = node;
177                 }
178         }
179
180         return obj->size;
181 }
182
183
184 void object_shift_index(object* obj, unsigned long index) {
185         assert(obj && index <= MAX_OBJECT_NODES);
186         if(obj->data == NULL) {
187                 obj->size = 0;
188                 return;
189         }
190
191         object_node* data = obj->data;
192         while(data) {
193                 if(data->index >= index)
194                         data->index--;
195                 data = data->next;
196         }
197         obj->size--;
198 }
199
200 unsigned long object_remove_index(object* obj, unsigned long index) {
201         assert(obj != NULL && index <= MAX_OBJECT_NODES);
202         if(obj->data == NULL) return 0;
203
204         /* removing the first item in the list */
205         if(obj->data->index == index) {
206                 object_node* tmp = obj->data->next;
207                 free_object_node(obj->data);
208                 obj->data = tmp;
209                 object_shift_index(obj,index);
210                 return obj->size;
211         }
212
213
214         object_node* prev = obj->data;
215         object_node* cur = prev->next;
216
217         while(cur) {
218                 if(cur->index == index) {
219                         object_node* tmp = cur->next;
220                         free_object_node(cur);
221                         prev->next = tmp;
222                         object_shift_index(obj,index);
223                         break;
224                 }
225                 prev = cur;
226                 cur = cur->next;
227         }
228
229         return obj->size;       
230 }
231
232
233 unsigned long object_remove_key(object* obj, char* key) {
234         assert(obj && key);
235         if(obj->data == NULL) return 0;
236
237         /* removing the first item in the list */
238         if(!strcmp(obj->data->key, key)) {
239                 object_node* tmp = obj->data->next;
240                 free_object_node(obj->data);
241                 obj->data = tmp;
242                 if(!obj->data) 
243                         obj->size = 0;
244
245                 return obj->size;
246         }
247
248         object_node* prev = obj->data;
249         object_node* cur = prev->next;
250
251         while(cur) {
252                 if(!strcmp(cur->key,key)) {
253                         object_node* tmp = cur->next;
254                         free_object_node(cur);
255                         prev->next = tmp;
256                         obj->size--;
257                         break;
258                 }
259                 prev = cur;
260                 cur = cur->next;
261         }
262
263         return obj->size;
264 }
265
266
267 unsigned long object_add_key(object* obj, char* key, object* new_obj) {
268
269         assert(obj != NULL && key != NULL);
270         object_clear_type(obj);
271         obj->is_hash = 1;
272
273
274         if(new_obj == NULL) {
275                 new_obj = new_object(NULL);
276                 new_obj->is_null = 1;
277         }
278
279         object_node* node = new_object_node(new_obj);
280         node->key = strdup(key);
281         
282         if( obj->data == NULL ) {
283                 obj->data = node;
284                 obj->size++;
285
286         } else {
287
288                 /* replace the first node */
289                 if(!strcmp(obj->data->key, key)) {
290                         object_node* tmp = obj->data->next;
291                         free_object_node(obj->data);
292                         obj->data = node;
293                         node->next = tmp;
294
295                 } else {
296                 
297                         object_node* prev = obj->data;
298                         object_node* cur = prev->next;
299                         int inserted = 0;
300
301                         while(cur != NULL) {
302
303                                 /* replace an existing node */
304                                 if( !strcmp(cur->key, key) ) {
305                                         object_node* tmp = cur->next;
306                                         free_object_node(cur);
307                                         node->next = tmp;
308                                         prev->next = node;
309                                         inserted = 1;
310                                         break;
311                                 }
312                                         
313                                 prev = cur;
314                                 cur = cur->next;
315                         }
316
317                         /* shove on to the end */
318                         if(!inserted) {
319                                 prev->next = node;
320                                 obj->size++;
321                         }
322                 }
323         }
324
325         return obj->size;
326 }
327
328
329 void free_object(object* obj) {
330
331         if(obj == NULL) return;
332         if(obj->classname) free(obj->classname);
333         if(obj->comment) free(obj->comment);
334
335         while(obj->data) {
336                 object_node* tmp = obj->data->next;
337                 free_object_node(obj->data);
338                 obj->data = tmp;
339         }
340
341         if(obj->string_data) 
342                 free(obj->string_data);
343         free(obj);
344 }
345
346 void free_object_node(object_node* node) {
347         if(node == NULL) return;
348         if(node->key) free(node->key);
349         free_object(node->item);
350         free(node);
351 }
352
353 object* object_get_index( object* obj, unsigned long index ) {
354         assert(obj != NULL && index <= MAX_OBJECT_NODES);
355         object_node* node = obj->data;
356         while(node) {
357                 if(node->index == index)
358                         return node->item;
359                 node = node->next;
360         }
361         return NULL;
362 }
363
364 object* object_get_key( object* obj, char* key ) {
365         assert(obj && key);
366         object_node* node = obj->data;
367
368         while(node) {
369                 if(node->key && !strcmp(node->key, key))
370                         return node->item;
371                 node = node->next;
372         }       
373
374         return NULL;
375 }
376
377 char* object_get_string(object* obj) {
378         assert(obj != NULL);
379         return obj->string_data;
380 }
381
382 void object_set_string(object* obj, char* string) {
383         assert(obj);
384         object_clear_type(obj);
385         obj->is_string = 1;
386         if(string)
387                 obj->string_data = strdup(string);
388 }
389
390
391 void object_set_number(object* obj, long num) {
392         assert(obj);
393         object_clear_type(obj);
394         obj->is_number = 1;
395         obj->num_value = num;
396 }
397
398 void object_set_double(object* obj, double num) {
399         assert(obj);
400         object_clear_type(obj);
401         obj->is_double = 1;
402         obj->double_value = num;
403 }
404
405
406 void object_set_class(object* obj, char* classname) {
407         assert(obj && classname);
408         obj->classname = strdup(classname);
409 }
410
411
412
413 char* object_to_json(object* obj) {
414
415         if(obj == NULL)
416                 return strdup("null");
417
418         growing_buffer* buf = buffer_init(64);
419
420         /* add class hints if we have a class name */
421         if(obj->classname) {
422                 buffer_add(buf,"/*--S ");
423                 buffer_add(buf,obj->classname);
424                 buffer_add(buf, "--*/");
425         }
426
427         if(obj->is_bool && obj->bool_value)
428                         buffer_add(buf, "true"); 
429
430         else if(obj->is_bool && ! obj->bool_value)
431                         buffer_add(buf, "false"); 
432
433         else if(obj->is_number) {
434                 char b[128];
435                 memset(b, 0, 128);
436                 sprintf(b, "%ld", obj->num_value);
437                 buffer_add(buf, b);
438         }
439
440         else if(obj->is_double) {
441                 char b[128];
442                 memset(b, 0, 128);
443                 sprintf(b, "%lf", obj->double_value);
444                 buffer_add(buf, b);
445         }
446
447         else if(obj->is_null)
448                 buffer_add(buf, "null");
449
450         else if (obj->is_string) {
451
452                 buffer_add(buf, "\"");
453                 char* data = obj->string_data;
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
461         }  else if(obj->is_array) {
462
463                 buffer_add(buf, "[");
464                 int i;
465                 for( i = 0; i!= obj->size; i++ ) {
466                         char* data = object_to_json(obj->get_index(obj,i));
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
481         } else if(obj->is_hash) {
482
483                 buffer_add(buf, "{");
484                 object_iterator* itr = new_iterator(obj);
485                 object_node* tmp;
486
487                 while( (tmp = itr->next(itr)) ) {
488                         buffer_add(buf, "\"");
489                         buffer_add(buf, tmp->key);
490                         buffer_add(buf, "\":");
491                         char* data =  object_to_json(tmp->item);
492
493 #ifdef STRICT_JSON_WRITE
494                         buffer_add(buf, data);
495 #else
496                         if(strcmp(data,"null")) /* only add the string if it isn't null */
497                                 buffer_add(buf, data);
498 #endif
499
500                         if(itr->has_next(itr))
501                                 buffer_add(buf, ",");
502                         free(data);
503                 }
504                 free_iterator(itr);
505                 buffer_add(buf, "}");
506         }
507
508         /* close out the object hint */
509         if(obj->classname) {
510                 buffer_add(buf, "/*--E ");
511                 buffer_add(buf, obj->classname);
512                 buffer_add(buf, "--*/");
513         }
514
515         if(obj->comment) {
516                 buffer_add(buf, " /*");
517                 buffer_add(buf, obj->comment);
518                 buffer_add(buf, "*/");
519         }
520
521         char* data = buffer_data(buf);
522         buffer_free(buf);
523         return data;
524
525 }
526
527
528 void object_clear_type(object* obj) {
529         if(obj == NULL) return;
530         obj->is_string = 0;
531         obj->is_hash    = 0;
532         obj->is_array   = 0;
533         obj->is_bool    = 0;
534         obj->is_null    = 0;
535 }
536
537
538 void object_set_comment(object* obj, char* com) {
539         assert(obj && com);
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* json_string_format(char* string) {
554
555         if(!string) return strdup("");
556
557         growing_buffer* buf = buffer_init(64);
558         int i;
559         int depth = 0;
560
561         for(i=0; i!= strlen(string); i++) {
562
563                 if( string[i] == '{' || string[i] == '[' ) {
564
565                         char* 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                         char* 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                         char* 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 object* object_clone(object* o) {
599         if(!o) return NULL;
600         char* json = o->to_json(o);
601         object* newo = json_parse_string(json);
602         free(json);
603         return newo;
604 }
605
606
607
608 /* ---------------------------------------------------------------------- */
609 /* Iterator */
610
611 object_iterator* new_iterator(object* obj) {
612         object_iterator* iter = safe_malloc(sizeof(object_iterator));
613         iter->obj = obj;
614         iter->current = obj->data;
615         iter->next = &object_iterator_next;
616         iter->has_next = &object_iterator_has_next;
617         return iter;
618 }
619
620 object_node* object_iterator_next(object_iterator* itr) {
621         assert( itr != NULL );
622
623         object_node* tmp = itr->current;
624         if(tmp == NULL) return NULL;
625         itr->current = itr->current->next;
626
627         return tmp;
628 }
629
630 void free_iterator(object_iterator* iter) { 
631         if(iter == NULL) return;
632         free(iter);
633 }
634
635 int object_iterator_has_next(object_iterator* itr) {
636         assert(itr);
637         if(itr->current) return 1;
638         return 0;
639 }
640
641