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