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* jsonObjectFindPath( const jsonObject* obj, const char* format, ...) {
168 if(!obj || !format || strlen(format) < 1) return NULL;
170 VA_LIST_TO_STRING(format);
173 char* tt; /* strtok storage */
175 /* special case where path starts with // (start anywhere) */
176 if(buf[0] == '/' && buf[1] == '/' && buf[2] != '\0') {
178 /* copy the path before strtok_r destroys it */
179 char* pathcopy = strdup(buf);
181 /* grab the root of the path */
182 token = strtok_r(buf, "/", &tt);
188 jsonObject* it = findMultiPath(obj, token, pathcopy + 1);
194 /* grab the root of the path */
195 token = strtok_r(buf, "/", &tt);
196 if(!token) return NULL;
199 obj = jsonObjectGetKeyConst(obj, token);
200 } while( (token = strtok_r(NULL, "/", &tt)) && obj);
202 return jsonObjectClone(obj);
206 /* --------------------------------------------------------------- */
208 /* Utility method. finds any object in the tree that matches the path.
209 Use this for finding paths that start with '//' */
210 static jsonObject* findMultiPath(const jsonObject* obj,
211 const char* root, const char* path) {
213 if(!obj || ! root || !path) return NULL;
215 /* collect all of the potential objects */
216 jsonObject* arr = findMultiPathRecurse(obj, root);
218 /* path is just /root or /root/ */
219 if( strlen(root) + 2 >= strlen(path) ) {
224 /* container for fully matching objects */
225 jsonObject* newarr = jsonNewObjectType(JSON_ARRAY);
228 /* gather all of the sub-objects that match the full path */
229 for( i = 0; i < arr->size; i++ ) {
230 const jsonObject* a = jsonObjectGetIndex(arr, i);
231 jsonObject* thing = jsonObjectFindPath(a , path + strlen(root) + 1);
233 if(thing) { //jsonObjectPush(newarr, thing);
234 if(thing->type == JSON_ARRAY) {
236 for( i = 0; i != thing->size; i++ )
237 jsonObjectPush(newarr, jsonObjectClone(jsonObjectGetIndex(thing,i)));
238 jsonObjectFree(thing);
240 jsonObjectPush(newarr, thing);
250 /* returns a list of object whose key is 'root'. These are used as
251 potential objects when doing a // search */
252 static jsonObject* findMultiPathRecurse(const jsonObject* obj, const char* root) {
254 jsonObject* arr = jsonNewObjectType(JSON_ARRAY);
259 /* if the current object has a node that matches, add it */
261 const jsonObject* o = jsonObjectGetKeyConst(obj, root);
262 if(o) jsonObjectPush( arr, jsonObjectClone(o) );
264 jsonObject* tmp = NULL;
265 jsonObject* childarr;
266 jsonIterator* itr = jsonNewIterator(obj);
268 /* recurse through the children and find all potential nodes */
269 while( (tmp = jsonIteratorNext(itr)) ) {
270 childarr = findMultiPathRecurse(tmp, root);
271 if(childarr && childarr->size > 0) {
272 for( i = 0; i!= childarr->size; i++ ) {
273 jsonObjectPush( arr, jsonObjectClone(jsonObjectGetIndex(childarr, i)) );
276 jsonObjectFree(childarr);
279 jsonIteratorFree(itr);