Continued const-correctness improvement from Scott McKellar:
[OpenSRF.git] / src / libopensrf / osrf_json_tools.c
1 /*
2 Copyright (C) 2006  Georgia Public Library Service 
3 Bill Erickson <billserickson@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 <opensrf/osrf_json.h>
17
18 jsonObject* _jsonObjectEncodeClass( const jsonObject* obj, int ignoreClass );
19
20
21 jsonObject* jsonObjectFindPath( const jsonObject* obj, char* path, ...);
22 jsonObject* _jsonObjectFindPathRecurse(const jsonObject* obj, char* root, char* path);
23 jsonObject* __jsonObjectFindPathRecurse(const jsonObject* obj, char* root);
24
25
26 static char* __tabs(int count) {
27         growing_buffer* buf = buffer_init(24);
28         int i;
29         for(i=0;i<count;i++) OSRF_BUFFER_ADD(buf, "  ");
30     return buffer_release(buf);
31 }
32
33 char* jsonFormatString( const char* string ) {
34         if(!string) return strdup("");
35
36         growing_buffer* buf = buffer_init(64);
37         int i;
38         int depth = 0;
39         char* tab = NULL;
40
41         char c;
42         for(i=0; i!= strlen(string); i++) {
43                 c = string[i];
44
45                 if( c == '{' || c == '[' ) {
46
47                         tab = __tabs(++depth);
48                         buffer_fadd( buf, "%c\n%s", c, tab);
49                         free(tab);
50
51                 } else if( c == '}' || c == ']' ) {
52
53                         tab = __tabs(--depth);
54                         buffer_fadd( buf, "\n%s%c", tab, c);
55                         free(tab);
56
57                         if(string[i+1] != ',') {
58                                 tab = __tabs(depth);
59                                 buffer_fadd( buf, "%s", tab );  
60                                 free(tab);
61                         }
62
63                 } else if( c == ',' ) {
64
65                         tab = __tabs(depth);
66                         buffer_fadd(buf, ",\n%s", tab);
67                         free(tab);
68
69                 } else { buffer_add_char(buf, c); }
70
71         }
72
73     return buffer_release(buf);
74 }
75
76
77
78 jsonObject* jsonObjectDecodeClass( const jsonObject* obj ) {
79         if(!obj) return jsonNewObject(NULL);
80
81         jsonObject* newObj                       = NULL; 
82         const jsonObject* classObj       = NULL;
83         const jsonObject* payloadObj = NULL;
84         int i;
85
86         if( obj->type == JSON_HASH ) {
87
88                 /* are we a special class object? */
89                 if( (classObj = jsonObjectGetKeyConst( obj, JSON_CLASS_KEY )) ) {
90
91                         /* do we have a payload */
92                         if( (payloadObj = jsonObjectGetKeyConst( obj, JSON_DATA_KEY )) ) {
93                                 newObj = jsonObjectDecodeClass( payloadObj ); 
94                                 jsonObjectSetClass( newObj, jsonObjectGetString(classObj) );
95
96                         } else { /* class is defined but there is no payload */
97                                 return NULL;
98                         }
99
100                 } else { /* we're a regular hash */
101
102                         jsonIterator* itr = jsonNewIterator(obj);
103                         jsonObject* tmp;
104                         newObj = jsonNewObjectType(JSON_HASH);
105                         while( (tmp = jsonIteratorNext(itr)) ) {
106                                 jsonObject* o = jsonObjectDecodeClass(tmp);
107                                 jsonObjectSetKey( newObj, itr->key, o );
108                         }
109                         jsonIteratorFree(itr);
110                 }
111
112         } else {
113
114                 if( obj->type == JSON_ARRAY ) { /* we're an array */
115                         newObj = jsonNewObjectType(JSON_ARRAY);
116                         for( i = 0; i != obj->size; i++ ) {
117                                 jsonObject* tmp = jsonObjectDecodeClass(jsonObjectGetIndex( obj, i ) );
118                                 jsonObjectSetIndex( newObj, i, tmp );
119                         }
120
121                 } else { /* not an aggregate type */
122                         newObj = jsonObjectClone(obj);
123                 }
124         }
125                 
126         return newObj;
127 }
128
129 jsonObject* jsonObjectEncodeClass( const jsonObject* obj ) {
130         return _jsonObjectEncodeClass( obj, 0 );
131 }
132
133 jsonObject* _jsonObjectEncodeClass( const jsonObject* obj, int ignoreClass ) {
134
135         //if(!obj) return NULL;
136         if(!obj) return jsonNewObject(NULL);
137         jsonObject* newObj = NULL;
138
139         if( obj->classname && ! ignoreClass ) {
140                 newObj = jsonNewObjectType(JSON_HASH);
141
142                 jsonObjectSetKey( newObj, 
143                         JSON_CLASS_KEY, jsonNewObject(obj->classname) ); 
144
145                 jsonObjectSetKey( newObj, 
146                         JSON_DATA_KEY, _jsonObjectEncodeClass(obj, 1));
147
148         } else if( obj->type == JSON_HASH ) {
149
150                 jsonIterator* itr = jsonNewIterator(obj);
151                 jsonObject* tmp;
152                 newObj = jsonNewObjectType(JSON_HASH);
153
154                 while( (tmp = jsonIteratorNext(itr)) ) {
155                         jsonObjectSetKey( newObj, itr->key, 
156                                         _jsonObjectEncodeClass(tmp, 0));
157                 }
158                 jsonIteratorFree(itr);
159
160         } else if( obj->type == JSON_ARRAY ) {
161
162                 newObj = jsonNewObjectType(JSON_ARRAY);
163                 int i;
164                 for( i = 0; i != obj->size; i++ ) {
165                         jsonObjectSetIndex( newObj, i, 
166                                 _jsonObjectEncodeClass(jsonObjectGetIndex( obj, i ), 0 ));
167                 }
168
169         } else {
170                 newObj = jsonObjectClone(obj);
171         }
172
173         return newObj;
174 }
175
176 jsonObject* jsonParseFile( char* filename ) {
177         if(!filename) return NULL;
178         char* data = file_to_string(filename);
179         jsonObject* o = jsonParseString(data);
180         free(data);
181         return o;
182 }
183
184
185
186 jsonObject* jsonObjectFindPath( const jsonObject* obj, char* format, ...) {
187         if(!obj || !format || strlen(format) < 1) return NULL;  
188
189         VA_LIST_TO_STRING(format);
190         char* buf = VA_BUF;
191         char* token = NULL;
192         char* t = buf;
193         char* tt; /* strtok storage */
194
195         /* copy the path before strtok_r destroys it */
196         char* pathcopy = strdup(buf);
197
198         /* grab the root of the path */
199         token = strtok_r(t, "/", &tt);
200         if(!token) return NULL;
201
202         /* special case where path starts with //  (start anywhere) */
203         if(strlen(pathcopy) > 2 && pathcopy[0] == '/' && pathcopy[1] == '/') {
204                 jsonObject* it = _jsonObjectFindPathRecurse(obj, token, pathcopy + 1);
205                 free(pathcopy);
206                 return it;
207         }
208
209         free(pathcopy);
210
211         t = NULL;
212         do { 
213                 obj = jsonObjectGetKey(obj, token);
214         } while( (token = strtok_r(NULL, "/", &tt)) && obj);
215
216         return jsonObjectClone(obj);
217 }
218
219 /* --------------------------------------------------------------- */
220
221
222
223 jsonObject* _jsonObjectFindPathRecurse(const jsonObject* obj, char* root, char* path) {
224
225         if(!obj || ! root || !path) return NULL;
226
227         /* collect all of the potential objects */
228         jsonObject* arr = __jsonObjectFindPathRecurse(obj, root);
229
230         /* container for fully matching objects */
231         jsonObject* newarr = jsonParseString("[]");
232         int i;
233
234         /* path is just /root or /root/ */
235         if( strlen(root) + 2 >= strlen(path) ) {
236                 return arr;
237
238         } else {
239
240                 /* gather all of the sub-objects that match the full path */
241                 for( i = 0; i < arr->size; i++ ) {
242                         const jsonObject* a = jsonObjectGetIndex(arr, i);
243                         jsonObject* thing = jsonObjectFindPath(a , path + strlen(root) + 1); 
244
245                         if(thing) { //jsonObjectPush(newarr, thing);
246                 if(thing->type == JSON_ARRAY) {
247                 int i;
248                                         for( i = 0; i != thing->size; i++ )
249                                                 jsonObjectPush(newarr, jsonObjectClone(jsonObjectGetIndex(thing,i)));
250                                         jsonObjectFree(thing);
251
252                                 } else {
253                                         jsonObjectPush(newarr, thing);
254                                 }                                               
255                         }
256                 }
257         }
258         
259         jsonObjectFree(arr);
260         return newarr;
261 }
262
263 jsonObject* __jsonObjectFindPathRecurse(const jsonObject* obj, char* root) {
264
265         jsonObject* arr = jsonParseString("[]");
266         if(!obj) return arr;
267
268         int i;
269
270         /* if the current object has a node that matches, add it */
271
272         jsonObject* o = jsonObjectGetKey(obj, root);
273         if(o) jsonObjectPush( arr, jsonObjectClone(o) );
274
275         jsonObject* tmp = NULL;
276         jsonObject* childarr;
277         jsonIterator* itr = jsonNewIterator(obj);
278
279         /* recurse through the children and find all potential nodes */
280         while( (tmp = jsonIteratorNext(itr)) ) {
281                 childarr = __jsonObjectFindPathRecurse(tmp, root);
282                 if(childarr && childarr->size > 0) {
283                         for( i = 0; i!= childarr->size; i++ ) {
284                                 jsonObjectPush( arr, jsonObjectClone(jsonObjectGetIndex(childarr, i)) );
285                         }
286                 }
287                 jsonObjectFree(childarr);
288         }
289
290         jsonIteratorFree(itr);
291
292         return arr;
293 }
294
295
296
297