2 Copyright (C) 2006 Georgia Public Library Service
3 Bill Erickson <billserickson@gmail.com>
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.
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.
16 #include <opensrf/osrf_json.h>
17 #include <opensrf/osrf_json_utils.h>
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 );
24 static void append_indentation( growing_buffer* buf, int depth ) {
27 memset( indent, ' ', n );
28 buffer_add_n( buf, indent, n );
31 char* jsonFormatString( const char* string ) {
32 if(!string) return strdup("");
34 growing_buffer* buf = buffer_init(64);
39 for(i = 0; string[i]; i++) {
42 if( c == '{' || c == '[' ) {
44 OSRF_BUFFER_ADD_CHAR( buf, c );
45 OSRF_BUFFER_ADD_CHAR( buf, '\n' );
46 append_indentation( buf, ++depth );
48 } else if( c == '}' || c == ']' ) {
50 OSRF_BUFFER_ADD_CHAR( buf, '\n' );
51 append_indentation( buf, --depth );
52 OSRF_BUFFER_ADD_CHAR( buf, c );
54 } else if( c == ',' ) {
56 OSRF_BUFFER_ADD_CHAR( buf, ',' );
57 OSRF_BUFFER_ADD_CHAR( buf, '\n' );
58 append_indentation( buf, depth );
61 OSRF_BUFFER_ADD_CHAR(buf, c);
64 return buffer_release(buf);
69 jsonObject* jsonObjectDecodeClass( const jsonObject* obj ) {
70 if(!obj) return jsonNewObject(NULL);
72 jsonObject* newObj = NULL;
73 const jsonObject* classObj = NULL;
74 const jsonObject* payloadObj = NULL;
77 if( obj->type == JSON_HASH ) {
79 /* are we a special class object? */
80 if( (classObj = jsonObjectGetKeyConst( obj, JSON_CLASS_KEY )) ) {
82 /* do we have a payload */
83 if( (payloadObj = jsonObjectGetKeyConst( obj, JSON_DATA_KEY )) ) {
84 newObj = jsonObjectDecodeClass( payloadObj );
85 jsonObjectSetClass( newObj, jsonObjectGetString(classObj) );
87 } else { /* class is defined but there is no payload */
91 } else { /* we're a regular hash */
93 jsonIterator* itr = jsonNewIterator(obj);
95 newObj = jsonNewObjectType(JSON_HASH);
96 while( (tmp = jsonIteratorNext(itr)) ) {
97 jsonObject* o = jsonObjectDecodeClass(tmp);
98 jsonObjectSetKey( newObj, itr->key, o );
100 jsonIteratorFree(itr);
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 );
112 } else { /* not an aggregate type */
113 newObj = jsonObjectClone(obj);
120 jsonObject* jsonObjectEncodeClass( const jsonObject* obj ) {
121 return _jsonObjectEncodeClass( obj, 0 );
124 static jsonObject* _jsonObjectEncodeClass( const jsonObject* obj, int ignoreClass ) {
126 //if(!obj) return NULL;
127 if(!obj) return jsonNewObject(NULL);
128 jsonObject* newObj = NULL;
130 if( obj->classname && ! ignoreClass ) {
131 newObj = jsonNewObjectType(JSON_HASH);
133 jsonObjectSetKey( newObj,
134 JSON_CLASS_KEY, jsonNewObject(obj->classname) );
136 jsonObjectSetKey( newObj,
137 JSON_DATA_KEY, _jsonObjectEncodeClass(obj, 1));
139 } else if( obj->type == JSON_HASH ) {
141 jsonIterator* itr = jsonNewIterator(obj);
143 newObj = jsonNewObjectType(JSON_HASH);
145 while( (tmp = jsonIteratorNext(itr)) ) {
146 jsonObjectSetKey( newObj, itr->key,
147 _jsonObjectEncodeClass(tmp, 0));
149 jsonIteratorFree(itr);
151 } else if( obj->type == JSON_ARRAY ) {
153 newObj = jsonNewObjectType(JSON_ARRAY);
155 for( i = 0; i != obj->size; i++ ) {
156 jsonObjectSetIndex( newObj, i,
157 _jsonObjectEncodeClass(jsonObjectGetIndex( obj, i ), 0 ));
161 newObj = jsonObjectClone(obj);
167 jsonObject* jsonParseFile( const char* filename ) {
168 if(!filename) return NULL;
169 char* data = file_to_string(filename);
170 jsonObject* o = jsonParseString(data);
177 jsonObject* jsonObjectFindPath( const jsonObject* obj, const char* format, ...) {
178 if(!obj || !format || strlen(format) < 1) return NULL;
180 VA_LIST_TO_STRING(format);
183 char* tt; /* strtok storage */
185 /* special case where path starts with // (start anywhere) */
186 if(buf[0] == '/' && buf[1] == '/' && buf[2] != '\0') {
188 /* copy the path before strtok_r destroys it */
189 char* pathcopy = strdup(buf);
191 /* grab the root of the path */
192 token = strtok_r(buf, "/", &tt);
198 jsonObject* it = findMultiPath(obj, token, pathcopy + 1);
204 /* grab the root of the path */
205 token = strtok_r(buf, "/", &tt);
206 if(!token) return NULL;
209 obj = jsonObjectGetKeyConst(obj, token);
210 } while( (token = strtok_r(NULL, "/", &tt)) && obj);
212 return jsonObjectClone(obj);
216 /* --------------------------------------------------------------- */
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) {
223 if(!obj || ! root || !path) return NULL;
225 /* collect all of the potential objects */
226 jsonObject* arr = findMultiPathRecurse(obj, root);
228 /* path is just /root or /root/ */
229 if( strlen(root) + 2 >= strlen(path) ) {
234 /* container for fully matching objects */
235 jsonObject* newarr = jsonNewObjectType(JSON_ARRAY);
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);
243 if(thing) { //jsonObjectPush(newarr, thing);
244 if(thing->type == JSON_ARRAY) {
246 for( i = 0; i != thing->size; i++ )
247 jsonObjectPush(newarr, jsonObjectClone(jsonObjectGetIndex(thing,i)));
248 jsonObjectFree(thing);
250 jsonObjectPush(newarr, thing);
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) {
264 jsonObject* arr = jsonNewObjectType(JSON_ARRAY);
269 /* if the current object has a node that matches, add it */
271 const jsonObject* o = jsonObjectGetKeyConst(obj, root);
272 if(o) jsonObjectPush( arr, jsonObjectClone(o) );
274 jsonObject* tmp = NULL;
275 jsonObject* childarr;
276 jsonIterator* itr = jsonNewIterator(obj);
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)) );
286 jsonObjectFree(childarr);
289 jsonIteratorFree(itr);