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