Merging changes from branches/new-json2
authorerickson <erickson@9efc2488-bf62-4759-914b-345cdb29e865>
Fri, 20 Jul 2007 12:58:59 +0000 (12:58 +0000)
committererickson <erickson@9efc2488-bf62-4759-914b-345cdb29e865>
Fri, 20 Jul 2007 12:58:59 +0000 (12:58 +0000)
svn merge -r1006:1012 svn://svn.open-ils.org/OpenSRF/branches/new-json2
svn merge -r1018:1019 svn://svn.open-ils.org/OpenSRF/branches/new-json2
svn merge -r1022:1025 svn://svn.open-ils.org/OpenSRF/branches/new-json2
svn merge -r1026:1028 svn://svn.open-ils.org/OpenSRF/branches/new-json2
svn merge -r1036:1037 svn://svn.open-ils.org/OpenSRF/branches/new-json2
svn merge -r1039:1040 svn://svn.open-ils.org/OpenSRF/branches/new-json2
svn merge -r1041:1043 svn://svn.open-ils.org/OpenSRF/branches/new-json2
svn merge -r1045:1048 svn://svn.open-ils.org/OpenSRF/branches/new-json2

This moves OpenSRF to the new JSON wire protocol,
including:
    a new C parser
    a new osrfList based string_array module
    a compatibility layer for mimicking libobjson and legacy JSON I/O in the HTTP gateway
    a small JSON test program for profiling, etc.

git-svn-id: svn://svn.open-ils.org/OpenSRF/trunk@1051 9efc2488-bf62-4759-914b-345cdb29e865

41 files changed:
include/objson/json2xml.h
include/objson/json_parser.h
include/objson/object.h
include/objson/xml2json.h
include/opensrf/osrfConfig.h
include/opensrf/osrf_app_session.h
include/opensrf/osrf_application.h
include/opensrf/osrf_cache.h
include/opensrf/osrf_json.h [new file with mode: 0644]
include/opensrf/osrf_json_utils.h [new file with mode: 0644]
include/opensrf/osrf_json_xml.h [new file with mode: 0644]
include/opensrf/osrf_legacy_json.h [new file with mode: 0644]
include/opensrf/osrf_message.h
include/opensrf/osrf_settings.h
include/opensrf/string_array.h
include/opensrf/utils.h
include/opensrf/xml_utils.h
install.conf
src/Makefile
src/c-apps/Makefile
src/c-apps/osrf_dbmath.c
src/c-apps/osrf_math.c
src/c-apps/osrf_version.c
src/gateway/Makefile
src/gateway/osrf_json_gateway.c
src/jserver/Makefile
src/libopensrf/Makefile
src/libopensrf/Makefile.json [new file with mode: 0644]
src/libopensrf/osrf_application.c
src/libopensrf/osrf_hash.c
src/libopensrf/osrf_json_object.c [new file with mode: 0644]
src/libopensrf/osrf_json_parser.c [new file with mode: 0644]
src/libopensrf/osrf_json_test.c [new file with mode: 0644]
src/libopensrf/osrf_json_tools.c [new file with mode: 0644]
src/libopensrf/osrf_json_xml.c [new file with mode: 0644]
src/libopensrf/osrf_legacy_json.c [new file with mode: 0644]
src/libopensrf/string_array.c
src/perlmods/OpenSRF/Utils/JSON.pm
src/router/Makefile
src/srfsh/Makefile
src/srfsh/srfsh.c

index 9eaa1c0..6539adf 100644 (file)
@@ -1,11 +1,10 @@
-
-#include <string.h>
-#include <stdio.h>
-
-/* the JSON parser, so we can read the response we're XMLizing */
-#include <objson/object.h>
-#include <objson/json_parser.h>
-#include <opensrf/utils.h>
-
-char* jsonObjectToXML(jsonObject*);
+/*
+ * Header to support legacy objson library
+ */
+#ifndef OBJSON_JSON2XML_H
+#define OBJSON_JSON2XML_H
+#include <opensrf/osrf_json.h>
+#include <opensrf/osrf_json_xml.h>
+#include <opensrf/osrf_legacy_json.h>
+#endif
 
index a1f785b..2c982ee 100644 (file)
@@ -1,84 +1,9 @@
 /*
-Copyright (C) 2005  Georgia Public Library Service 
-Bill Erickson <highfalutin@gmail.com>
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-*/
-
-
-
-
-/* ---------------------------------------------------------------------------------------
-       JSON parser.
- * --------------------------------------------------------------------------------------- */
-#ifndef JSON_PARSER_H
-#define JSON_PARSER_H
-
-#include <stdio.h>
-#include <ctype.h>
-#include <objson/object.h>
-#include <opensrf/utils.h>
-
-
-
-/* Parses the given JSON string and returns the built object. 
- *     returns NULL (and prints parser error to stderr) on error.  
+ * Header to support legacy objson library
  */
-
-jsonObject* json_parse_string(char* string);
-
-jsonObject* jsonParseString(char* string);
-jsonObject* jsonParseStringFmt( char* string, ... );
-
-jsonObject* json_parse_file( const char* filename );
-
-jsonObject* jsonParseFile( const char* string );
-
-
-
-/* does the actual parsing work.  returns 0 on success.  -1 on error and
- * -2 if there was no object to build (string was all comments) 
- */
-int _json_parse_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
-
-/* returns 0 on success and turns obj into a string object */
-int json_parse_json_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
-
-/* returns 0 on success and turns obj into a number or double object */
-int json_parse_json_number(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
-
-/* returns 0 on success and turns obj into an 'object' object */
-int json_parse_json_object(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
-
-/* returns 0 on success and turns object into an array object */
-int json_parse_json_array(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
-
-/* churns through whitespace and increments index as it goes.
- * eat_all == true means we should eat newlines, tabs
- */
-void json_eat_ws(char* string, unsigned long* index, int eat_all, int current_strlen);
-
-int json_parse_json_bool(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
-
-/* removes comments from a json string.  if the comment contains a class hint
- * and class_hint isn't NULL, an allocated char* with the class name will be
- * shoved into *class_hint.  returns 0 on success, -1 on parse error.
- * 'index' is assumed to be at the second character (*) of the comment
- */
-int json_eat_comment(char* string, unsigned long* index, char** class_hint, int parse_class, int current_strlen);
-
-/* prints a useful error message to stderr. always returns -1 */
-int json_handle_error(char* string, unsigned long* index, char* err_msg);
-
-int json_parse_json_null(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
-
-
+#ifndef OBJSON_XML2JSON_H
+#define OBJSON_XML2JSON_H
+#include <opensrf/osrf_json.h>
+#include <opensrf/osrf_json_xml.h>
+#include <opensrf/osrf_legacy_json.h>
 #endif
index 8d62c1a..e54f8ed 100644 (file)
@@ -1,286 +1,8 @@
 /*
-Copyright (C) 2005  Georgia Public Library Service 
-Bill Erickson <highfalutin@gmail.com>
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-*/
-
-
-/* ---------------------------------------------------------------------------------------
-       libjson
- * --------------------------------------------------------------------------------------- */
-
-#ifndef _JSON_OBJECT_H
-#define _JSON_OBJECT_H
-
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <opensrf/utils.h>
-
-/* json object types */
-#define JSON_HASH      0
-#define JSON_ARRAY     1
-#define JSON_STRING    2
-#define JSON_NUMBER    3
-#define JSON_NULL      4       
-#define JSON_BOOL      5
-
-
-/* top level generic object structure */
-struct _jsonObjectStruct {
-
-       /* how many sub-objects do we contain if we're an array or an object.  
-               Note that this includes null array elements in sparse arrays */
-       unsigned long size;
-
-       /* optional class hint */
-       char* classname;
-
-       /* see JSON types above */
-       int type;
-
-
-       /* our cargo */
-       union _jsonObjectValue {
-               struct _jsonObjectNodeStruct* c; /* our list of sub-objects if we're an array or a hash */
-               char*           s; /* string */
-               int                     b; /* bool */
-               double          n; /* number */
-       } value;
-       
-
-       /* client may provide a comment string which will be 
-        * added to the object when stringified */
-       char* comment;
-
-};
-typedef struct _jsonObjectStruct jsonObject;
-
-
-/** 
-       String parsing function.  This is assigned by the json_parser code.
-       to avoid circular dependency, declare the parse function here,
-       and have the json parse code set the variable to a real function 
-*/
-//jsonObject* (*jsonParseString) (char* str);
-
-
-/* this contains a single element of the object along with the elements 
- * index (if this object is an array) and key (if this object is a hash)
+ * Header to support legacy objson library
  */
-struct _jsonObjectNodeStruct {
-
-       unsigned long index; /* our array position */
-       char* key; /* our hash key */
-
-       jsonObject* item; /* our object */
-       struct _jsonObjectNodeStruct* next; /* pointer to the next object node */
-};
-typedef struct _jsonObjectNodeStruct jsonObjectNode;
-
-
-
-/* utility object for iterating over hash objects */
-struct _jsonObjectIteratorStruct {
-       const jsonObject* obj; /* the topic object */
-       jsonObjectNode* current; /* the current node within the object */
-};
-typedef struct _jsonObjectIteratorStruct jsonObjectIterator;
-
-
-/** Allocates a new iterator 
-       @param obj The object over which to iterate.
-*/
-jsonObjectIterator* jsonNewObjectIterator(const jsonObject* obj);
-
-/** 
-       De-allocates an iterator 
-       @param iter The iterator object to free
-*/
-void jsonObjectIteratorFree(jsonObjectIterator* iter);
-
-/** 
-       Returns the object_node currently pointed to by the iterator
-       and increments the pointer to the next node
-       @param iter The iterator in question.
- */
-jsonObjectNode* jsonObjectIteratorNext(jsonObjectIterator* iter);
-
-/** 
-       @param iter The iterator.
-       @return True if there is another node after the current node.
- */
-int jsonObjectIteratorHasNext(const jsonObjectIterator* iter);
-
-
-/** 
-       Allocates a new object. 
-       @param string The string data if this object is to be a string.  
-       if not, string should be NULL 
-       @return The newly allocated object or NULL on memory error.
-*/
-jsonObject* jsonNewObjectFmt(const char* string, ...);
-jsonObject* jsonNewObject(const char* string);
-
-/**
-       Allocates a new JSON number object.
-       @param num The number this object is to hold
-       @return The newly allocated object.
-*/
-jsonObject* jsonNewNumberObject( double num );
-
-
-/** 
-       Returns a pointer to the object at the given index.  This call is
-       only valid if the object has a type of JSON_ARRAY.
-       @param obj The object
-       @param index The position within the object
-       @return The object at the given index.
-*/
-jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index );
-
-
-/** 
-       Returns a pointer to the object with the given key 
-       @param obj The object
-       @param key The key
-       @return The object with the given key.
-*/
-jsonObject* jsonObjectGetKey( const jsonObject* obj, const char* key );
-
-/** 
-       De-allocates an object.  Note that this function should only be called 
-       on objects that are _not_ children of other objects or there will be
-       double-free's
-       @param obj The object to free.
-*/
-void jsonObjectFree(jsonObject* obj);
-
-
-/** 
-       Allocates a new object node.
-       @param obj The object to which the node will be appended.
-       @return The new object node.
-*/
-jsonObjectNode* jsonNewObjectNode(jsonObject* obj);
-
-/** 
-       De-allocates an object node 
-       @param obj The object node to de-allocate.
-*/
-void jsonObjectNodeFree(jsonObjectNode* obj);
-
-
-/** 
-       Pushes the given object onto the end of the list.  This coerces an object
-       into becoming an array.  _Only_ use this function on objects that you
-       want to become an array.
-       If obj is NULL, inserts a new NULL object into the list.
-       @return array size on success, -1 on error 
- */
-unsigned long jsonObjectPush(jsonObject* dest, jsonObject* newObj);
-
-/* removes (and deallocates) the object at the given index (if one exists) and inserts 
- * the new one.  returns the size on success, -1 on error 
- * If obj is NULL, inserts a new object into the list with is_null set to true
- */
-unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj);
-
-/* inserts the new object, overwriting (removing, deallocating) any 
- * previous object with the given key.
- * returns the size on success, -1 on error 
- * if 'obj' is NULL, a new object is inserted at key 'key' with 'is_null' 
- * set to true
- */
-unsigned long jsonObjectSetKey(jsonObject* dest, const char* key, jsonObject* newObj);
-
-/* removes the object at the given index and, if more items exist,
- * re-indexes (shifts down by 1) the rest of the objects in the array
- */
-unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index);
-
-/* removes (and deallocates) the object with key 'key' if it exists */
-unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key);
-
-/* returns a pointer to the string data held by this object if this object
-       is a string.  Otherwise returns NULL*/
-char* jsonObjectGetString(const jsonObject*);
-
-double jsonObjectGetNumber( const jsonObject* obj );
-
-/* sets the string data */
-void jsonObjectSetString(jsonObject* dest, const char* string);
-
-/* sets the number value for the object */
-void jsonObjectSetNumber(jsonObject* dest, double num);
-
-/* sets the class hint for this object */
-void jsonObjectSetClass(jsonObject* dest, const char* classname );
-
-/* converts an object to a json string.  client is responsible for freeing the return string */
-char* jsonObjectToJSON( const jsonObject* obj );
-
-/* set this object's comment string */
-void jsonObjectSetComment(jsonObject* dest, const char* classname);
-
-/* utility method.  starting at index 'index', shifts all indices down by one and 
- * decrements the objects size by 1 
- */
-void _jsonObjectShiftIndex(jsonObject* dest, unsigned long index);
-
-/* formats a JSON string from printing.  User must free returned string */
-char* jsonFormatString( const char* jsonString );
-
-jsonObject* jsonObjectClone( const jsonObject* o );
-
-/* tries to extract the string data from an object.
-       if object -> NULL (the C NULL)
-       if array ->     NULL  (the C NULL)
-       if null  -> NULL (the C NULL)
-       if true/false -> true/false
-       if string/number/double the string version of either of those
-       The caller is responsible for freeing the returned string
-       */
-char* jsonObjectToSimpleString( const jsonObject* o );
-
-int jsonBoolIsTrue( const jsonObject* o );
-
-
-/* ------------------------------------------------------------------------ */
-/* XPATH */
-
-/* provides an XPATH style search interface (e.g. /some/node/here) and 
-       return the object at that location if one exists.  Naturally,  
-       every element in the path must be a proper object ("hash" / {}).
-       Returns NULL if the specified node is not found 
-       Note also that the object returned is a clone and
-       must be freed by the caller
-*/
-jsonObject* jsonObjectFindPath( const jsonObject* obj, char* path, ... );
-
-
-/* Utility method. finds any object in the tree that matches the path.  
-       Use this for finding paths that start with '//' */
-jsonObject* _jsonObjectFindPathRecurse( const jsonObject* o, char* root, char* path );
-
-/* returns a list of object whose key is 'root'.  These are used as
-       potential objects when doing a // search */
-jsonObject* __jsonObjectFindPathRecurse( const jsonObject* o, char* root );
-
-/* ------------------------------------------------------------------------ */
-
-
+#ifndef OBJSON_OBJECT_H
+#define OBJSON_OBJECT_H
+#include <opensrf/osrf_json.h>
+#include <opensrf/osrf_legacy_json.h>
 #endif
-
-
index a9ded67..037fc75 100644 (file)
@@ -1,19 +1,11 @@
-
-#include <stdio.h>
-#include <string.h>
-#include <libxml/globals.h>
-#include <libxml/xmlerror.h>
-#include <libxml/parser.h>
-#include <libxml/tree.h>
-#include <libxml/xmlmemory.h>
-
-#include <objson/object.h>
-#include <objson/json_parser.h>
-#include <opensrf/utils.h>
-#include <opensrf/osrf_list.h>
-
-
-jsonObject* jsonXMLToJSONObject(const char* xml);
-
+/*
+ * Header to support legacy objson library
+ */
+#ifndef OBJSON_XML2JSON_H
+#define OBJSON_XML2JSON_H
+#include <opensrf/osrf_json.h>
+#include <opensrf/osrf_json_xml.h>
+#include <opensrf/osrf_legacy_json.h>
+#endif
 
 
index 75dbcfd..dbe59ed 100644 (file)
@@ -19,7 +19,7 @@ GNU General Public License for more details.
 #include <opensrf/xml_utils.h>
 #include <opensrf/utils.h>
 #include <opensrf/string_array.h>
-#include <objson/object.h>
+#include <opensrf/osrf_json.h>
 
 typedef struct {
        jsonObject* config;
index aae373c..3cdbcb6 100644 (file)
@@ -9,8 +9,7 @@
 #include <opensrf/osrf_hash.h>
 #include <opensrf/osrf_list.h>
 
-#include <objson/object.h>
-#include <objson/json_parser.h>
+#include <opensrf/osrf_json.h>
 
 
 
index ac548a2..20bb5e0 100644 (file)
@@ -3,7 +3,7 @@
 #include <opensrf/osrf_app_session.h>
 #include <opensrf/osrf_hash.h>
 
-#include <objson/object.h>
+#include <opensrf/osrf_json.h>
 #include <stdio.h>
 #include <dlfcn.h>
 
index 5a755ff..8960801 100644 (file)
@@ -14,8 +14,7 @@ GNU General Public License for more details.
 */
 
 
-#include <objson/object.h>
-#include <objson/json_parser.h>
+#include <opensrf/osrf_json.h>
 #include <memcache.h>
 #include <opensrf/log.h>
 
diff --git a/include/opensrf/osrf_json.h b/include/opensrf/osrf_json.h
new file mode 100644 (file)
index 0000000..6c6e489
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+Copyright (C) 2006  Georgia Public Library Service 
+Bill Erickson <billserickson@gmail.com>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+*/
+
+
+#include <opensrf/utils.h>
+#include <opensrf/osrf_list.h>
+#include <opensrf/osrf_hash.h>
+
+#ifndef _JSON_H
+#define _JSON_H
+
+
+/* parser states */
+#define JSON_STATE_IN_OBJECT   0x1
+#define JSON_STATE_IN_ARRAY            0x2
+#define JSON_STATE_IN_STRING   0x4
+#define JSON_STATE_IN_UTF              0x8
+#define JSON_STATE_IN_ESCAPE   0x10
+#define JSON_STATE_IN_KEY              0x20
+#define JSON_STATE_IN_NULL             0x40
+#define JSON_STATE_IN_TRUE             0x80
+#define JSON_STATE_IN_FALSE            0x100
+#define JSON_STATE_IN_NUMBER   0x200
+#define JSON_STATE_IS_INVALID  0x400
+#define JSON_STATE_IS_DONE             0x800
+#define JSON_STATE_START_COMMEN        0x1000
+#define JSON_STATE_IN_COMMENT  0x2000
+#define JSON_STATE_END_COMMENT 0x4000
+
+
+/* object and array (container) states are pushed onto a stack so we
+ * can keep track of the object nest.  All other states are
+ * simply stored in the state field of the parser */
+#define JSON_STATE_SET(ctx,s) ctx->state |= s; /* set a state */
+#define JSON_STATE_REMOVE(ctx,s) ctx->state &= ~s; /* unset a state */
+#define JSON_STATE_CHECK(ctx,s) (ctx->state & s) ? 1 : 0 /* check if a state is set */
+#define JSON_STATE_POP(ctx) osrfListPop( ctx->stateStack ); /* remove a state from the stack */
+#define JSON_STATE_PUSH(ctx, state) osrfListPush( ctx->stateStack,(void*) state );/* push a state on the stack */
+#define JSON_STATE_PEEK(ctx) osrfListGetIndex(ctx->stateStack, ctx->stateStack->size -1) /* check which container type we're currently in */
+#define JSON_STATE_CHECK_STACK(ctx, s) (JSON_STATE_PEEK(ctx) == (void*) s ) ? 1 : 0  /* compare stack values */
+
+/* JSON types */
+#define JSON_HASH      0
+#define JSON_ARRAY     1
+#define JSON_STRING    2
+#define JSON_NUMBER    3
+#define JSON_NULL      4       
+#define JSON_BOOL      5
+
+#define JSON_PARSE_LAST_CHUNK 0x1 /* this is the last part of the string we're parsing */
+
+#define JSON_PARSE_FLAG_CHECK(ctx, f) (ctx->flags & f) ? 1 : 0 /* check if a parser state is set */
+
+#ifndef JSON_CLASS_KEY
+#define JSON_CLASS_KEY "__c"
+#endif
+#ifndef JSON_DATA_KEY
+#define JSON_DATA_KEY "__p"
+#endif
+
+
+struct jsonParserContextStruct {
+       int state;                                              /* what are we currently parsing */
+       char* chunk;                                    /* the chunk we're currently parsing */
+       int index;                                              /* where we are in parsing the current chunk */
+       int chunksize;                                  /* the size of the current chunk */
+       int flags;                                              /* parser flags */
+       osrfList* stateStack;           /* represents the nest of object/array states */
+       growing_buffer* buffer;         /* used to hold JSON strings, number, true, false, and null sequences */
+       growing_buffer* utfbuf;         /* holds the current unicode characters */
+       void* userData;                         /* opaque user pointer.  we ignore this */
+       struct jsonParserHandlerStruct* handler; /* the event handler struct */
+};
+typedef struct jsonParserContextStruct jsonParserContext;
+
+struct jsonParserHandlerStruct {
+       void (*handleStartObject)       (void* userData);
+       void (*handleObjectKey)         (void* userData, char* key);
+       void (*handleEndObject)         (void* userData);
+       void (*handleStartArray)        (void* userData);
+       void (*handleEndArray)          (void* userData);
+       void (*handleNull)                      (void* userData);
+       void (*handleString)                    (void* userData, char* string);
+       void (*handleBool)                      (void* userData, int boolval);
+       void (*handleNumber)                    (void* userData, double num);
+       void (*handleError)                     (void* userData, char* err, ...);
+};
+typedef struct jsonParserHandlerStruct jsonParserHandler;
+
+struct _jsonObjectStruct {
+       unsigned long size;     /* number of sub-items */
+       char* classname;                /* optional class hint (not part of the JSON spec) */
+       int type;                               /* JSON type */
+       struct _jsonObjectStruct* parent;       /* who we're attached to */
+       union __jsonValue {     /* cargo */
+               osrfHash*       h;              /* object container */
+               osrfList*       l;              /* array container */
+               char*           s;              /* string */
+               int                     b;              /* bool */
+//             double  n;              /* number */
+               double  n;              /* number */
+       } value;
+};
+typedef struct _jsonObjectStruct jsonObject;
+
+struct _jsonIteratorStruct {
+       jsonObject* obj; /* the object we're traversing */
+       osrfHashIterator* hashItr; /* the iterator for this hash */
+       char* key; /* if this object is an object, the current key */
+       unsigned long index; /* if this object is an array, the index */
+};
+typedef struct _jsonIteratorStruct jsonIterator;
+
+
+
+/** 
+ * Allocates a new parser context object
+ * @param handler The event handler struct
+ * @param userData Opaque user pointer which is available in callbacks
+ * and ignored by the parser
+ * @return An allocated parser context, NULL on error
+ */
+jsonParserContext* jsonNewParser( jsonParserHandler* handler, void* userData);
+
+/**
+ * Deallocates a parser context
+ * @param ctx The context object
+ */
+void jsonParserFree( jsonParserContext* ctx );
+
+/**
+ * Parse a chunk of data.
+ * @param ctx The parser context
+ * @param data The data to parse
+ * @param datalen The size of the chunk to parser
+ * @param flags Reserved
+ */
+int jsonParseChunk( jsonParserContext* ctx, char* data, int datalen, int flags );
+
+
+/**
+ * Parses a JSON string;
+ * @param str The string to parser
+ * @return The resulting JSON object or NULL on error
+ */
+jsonObject* jsonParseString( char* str );
+jsonObject* jsonParseStringRaw( char* str );
+
+jsonObject* jsonParseStringFmt( char* str, ... );
+
+/**
+ * Parses a JSON string;
+ * @param str The string to parser
+ * @return The resulting JSON object or NULL on error
+ */
+jsonObject* jsonParseStringHandleError( void (*errorHandler) (const char*), char* str, ... );
+
+
+
+/**
+ * Creates a new json object
+ * @param data The string data this object will hold if 
+ * this object happens to be a JSON_STRING, NULL otherwise
+ * @return The allocated json object.  Must be freed with 
+ * jsonObjectFree()
+ */
+jsonObject* jsonNewObject(const char* data);
+jsonObject* jsonNewObjectFmt(const char* data, ...);
+
+/**
+ * Creates a new object of the given type
+ */
+jsonObject* jsonNewObjectType(int type);
+
+/**
+ * Creates a new number object
+ */
+jsonObject* jsonNewNumberObject( double num );
+
+
+/**
+ * Creates a new json bool
+ */
+jsonObject* jsonNewBoolObject(int val);
+
+/**
+ * Deallocates an object
+ */
+void jsonObjectFree( jsonObject* o );
+
+/**
+ * Forces the given object to become an array (if it isn't already one) 
+ * and pushes the new object into the array
+ */
+unsigned long jsonObjectPush(jsonObject* o, jsonObject* newo);
+
+/**
+ * Forces the given object to become a hash (if it isn't already one)
+ * and assigns the new object to the key of the hash
+ */
+unsigned long jsonObjectSetKey(
+               jsonObject* o, const char* key, jsonObject* newo);
+
+
+/**
+ * Turns the object into a JSON string.  The string must be freed by the caller */
+char* jsonObjectToJSON( const jsonObject* obj );
+char* jsonObjectToJSONRaw( const jsonObject* obj );
+
+
+/**
+ * Retrieves the object at the given key
+ */
+jsonObject* jsonObjectGetKey( const jsonObject* obj, const char* key );
+
+
+
+
+
+
+/** Allocates a new iterator 
+       @param obj The object over which to iterate.
+*/
+jsonIterator* jsonNewIterator(const jsonObject* obj);
+
+
+/** 
+       De-allocates an iterator 
+       @param iter The iterator object to free
+*/
+void jsonIteratorFree(jsonIterator* iter);
+
+/** 
+       Returns the object_node currently pointed to by the iterator
+       and increments the pointer to the next node
+       @param iter The iterator in question.
+ */
+jsonObject* jsonIteratorNext(jsonIterator* iter);
+
+
+/** 
+       @param iter The iterator.
+       @return True if there is another node after the current node.
+ */
+int jsonIteratorHasNext(const jsonIterator* iter);
+
+
+/** 
+       Returns a pointer to the object at the given index.  This call is
+       only valid if the object has a type of JSON_ARRAY.
+       @param obj The object
+       @param index The position within the object
+       @return The object at the given index.
+*/
+jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index );
+
+
+/* removes (and deallocates) the object at the given index (if one exists) and inserts 
+ * the new one.  returns the size on success, -1 on error 
+ * If obj is NULL, inserts a new object into the list with is_null set to true
+ */
+unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj);
+
+/* removes the object at the given index and, if more items exist,
+ * re-indexes (shifts down by 1) the rest of the objects in the array
+ */
+unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index);
+
+/* removes (and deallocates) the object with key 'key' if it exists */
+unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key);
+
+/* returns a pointer to the string data held by this object if this object
+       is a string.  Otherwise returns NULL*/
+char* jsonObjectGetString(const jsonObject*);
+
+double jsonObjectGetNumber( const jsonObject* obj );
+
+/* sets the string data */
+void jsonObjectSetString(jsonObject* dest, const char* string);
+
+/* sets the number value for the object */
+void jsonObjectSetNumber(jsonObject* dest, double num);
+
+/* sets the class hint for this object */
+void jsonObjectSetClass(jsonObject* dest, const char* classname );
+const char* jsonObjectGetClass(const jsonObject* dest);
+
+int jsonBoolIsTrue( jsonObject* boolObj );
+
+void jsonSetBool(jsonObject* bl, int val);
+
+jsonObject* jsonObjectClone( const jsonObject* o );
+
+
+/* tries to extract the string data from an object.
+       if object       -> NULL (the C NULL)
+       if array                ->      NULL  
+       if null         -> NULL 
+       if bool         -> NULL
+       if string/number the string version of either of those
+       The caller is responsible for freeing the returned string
+       */
+char* jsonObjectToSimpleString( const jsonObject* o );
+
+
+
+/* provides an XPATH style search interface (e.g. /some/node/here) and 
+       return the object at that location if one exists.  Naturally,  
+       every element in the path must be a proper object ("hash" / {}).
+       Returns NULL if the specified node is not found 
+       Note also that the object returned is a clone and
+       must be freed by the caller
+*/
+jsonObject* jsonObjectFindPath( const jsonObject* obj, char* path, ... );
+
+
+/* formats a JSON string from printing.  User must free returned string */
+char* jsonFormatString( const char* jsonString );
+
+/* sets the error handler for all parsers */
+void jsonSetGlobalErrorHandler(void (*errorHandler) (const char*));
+
+jsonObject* jsonParseFile( char* filename );
+
+/* ------------------------------------------------------------------------- */
+/**
+ * The following methods provide a ficility for serializing and
+ * deserializing "classed" JSON objects.  To give a JSON object a 
+ * class, simply call jsonObjectSetClass().  
+ * Then, calling jsonObjectEncodeClass() will convert the JSON
+ * object (and any sub-objects) to a JSON object with class 
+ * wrapper objects like so:
+ * { _c : "classname", _d : <json_thing> }
+ * In this example _c is the class key and _d is the data (object)
+ * key.  The keys are defined by the constants 
+ * OSRF_JSON_CLASS_KEY and OSRF_JSON_DATA_KEY
+ * To revive a serialized object, simply call
+ * jsonObjectDecodeClass()
+ */
+
+
+/** Converts a class-wrapped object into an object with the
+ * classname set
+ * Caller must free the returned object 
+ */ 
+jsonObject* jsonObjectDecodeClass( jsonObject* obj );
+
+
+/** Converts an object with a classname into a
+ * class-wrapped (serialized) object
+ * Caller must free the returned object 
+ */ 
+jsonObject* jsonObjectEncodeClass( jsonObject* obj );
+
+/* ------------------------------------------------------------------------- */
+
+
+/**
+ *     Generates an XML representation of a JSON object */
+char* jsonObjectToXML(jsonObject*);
+
+
+/*
+ * Builds a JSON object from the provided XML 
+ */
+jsonObject* jsonXMLToJSONObject(const char* xml);
+
+
+#endif
diff --git a/include/opensrf/osrf_json_utils.h b/include/opensrf/osrf_json_utils.h
new file mode 100644 (file)
index 0000000..db3c1aa
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+Copyright (C) 2006  Georgia Public Library Service 
+Bill Erickson <billserickson@gmail.com>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+*/
+
+
+/* ----------------------------------------------------------------------- */
+/* Clients need not include this file.  These are internal utilities only      */
+/* ----------------------------------------------------------------------- */
+
+#define JSON_EAT_WS(ctx)       \
+       while( ctx->index < ctx->chunksize ) {  \
+               if(!isspace(ctx->chunk[ctx->index])) break; \
+               ctx->index++;   \
+       } \
+       if( ctx->index >= ctx->chunksize ) return 0; \
+       c = ctx->chunk[ctx->index];
+
+#define JSON_CACHE_DATA(ctx, buf, size) \
+       while( (buf->n_used < size) && (ctx->index < ctx->chunksize) ) \
+               buffer_add_char(buf, ctx->chunk[ctx->index++]); 
+
+#define JSON_LOG_MARK __FILE__,__LINE__
+
+#define JSON_NUMBER_CHARS "0123456789.+-e"
+
+
+/* cleans up an object if it is morphing another object, also
+ * verifies that the appropriate storage container exists where appropriate */
+#define JSON_INIT_CLEAR(_obj_, newtype)                \
+       if( _obj_->type == JSON_HASH && newtype != JSON_HASH ) {                        \
+               osrfHashFree(_obj_->value.h);                   \
+               _obj_->value.h = NULL;                                  \
+       } else if( _obj_->type == JSON_ARRAY && newtype != JSON_ARRAY ) {       \
+               osrfListFree(_obj_->value.l);                   \
+               _obj_->value.l = NULL;                                  \
+       } else if( _obj_->type == JSON_STRING && newtype != JSON_STRING ) { \
+               free(_obj_->value.s);                                           \
+               _obj_->value.s = NULL;                                  \
+       } \
+       _obj_->type = newtype;\
+       if( newtype == JSON_HASH && _obj_->value.h == NULL ) {  \
+               _obj_->value.h = osrfNewHash();         \
+               _obj_->value.h->freeItem = _jsonFreeHashItem; \
+       } else if( newtype == JSON_ARRAY && _obj_->value.l == NULL ) {  \
+               _obj_->value.l = osrfNewList();         \
+               _obj_->value.l->freeItem = _jsonFreeListItem;\
+       }                                                                                               \
+
+
+/** 
+ * These are the callbacks through which the top level parser 
+ * builds objects via the push parser
+ */
+void _jsonHandleStartObject(void*);
+void _jsonHandleObjectKey(void*, char* key);
+void _jsonHandleEndObject(void*);
+void _jsonHandleStartArray(void*);
+void _jsonHandleEndArray(void*);
+void _jsonHandleNull(void*);
+void _jsonHandleString(void*, char* string);
+void _jsonHandleBool(void*, int boolval);
+void _jsonHandleNumber(void*, double num);
+void _jsonHandleError(void*, char* str, ...);
+
+struct jsonInternalParserStruct {
+       jsonParserContext* ctx;
+       jsonObject* obj;
+       jsonObject* current;
+       char* lastkey;
+       void (*handleError) (const char*);
+};
+typedef struct jsonInternalParserStruct jsonInternalParser;
+
+jsonInternalParser* _jsonNewInternalParser();
+void _jsonInternalParserFree(jsonInternalParser* p);
+
+/**
+ * Calls the defined error handler with the given error message.
+ * @return -1
+ */
+int _jsonParserError( jsonParserContext* ctx, char* err, ... );
+
+
+/**
+ *
+ * @return 0 on continue, 1 if it goes past the end of the string, -1 on error
+ */
+int _jsonParserHandleUnicode( jsonParserContext* ctx );
+
+
+/**
+ * @param type 0 for null, 1 for true, 2 for false
+ * @return 0 on continue, 1 if it goes past the end of the string, -1 on error
+ */
+int _jsonParserHandleMatch( jsonParserContext* ctx, int type );
+
+/**
+ * @return 0 on continue, 1 on end of chunk, -1 on error 
+ */
+int _jsonParserHandleString( jsonParserContext* ctx );
+
+/**
+ * @return 0 on continue, 1 on end of chunk, -1 on error 
+ */
+int _jsonParserHandleNumber( jsonParserContext* ctx );
+
+
+void _jsonInsertParserItem( jsonInternalParser* p, jsonObject* newo );
+
+
+/* Utility method. finds any object in the tree that matches the path.  
+       Use this for finding paths that start with '//' */
+jsonObject* _jsonObjectFindPathRecurse( const jsonObject* o, char* root, char* path );
+
+
+/* returns a list of object whose key is 'root'.  These are used as
+       potential objects when doing a // search */
+jsonObject* __jsonObjectFindPathRecurse( const jsonObject* o, char* root );
+
+
diff --git a/include/opensrf/osrf_json_xml.h b/include/opensrf/osrf_json_xml.h
new file mode 100644 (file)
index 0000000..c01b1ac
--- /dev/null
@@ -0,0 +1,26 @@
+#ifdef OSRF_JSON_ENABLE_XML_UTILS
+
+#include <stdio.h>
+#include <string.h>
+#include <libxml/globals.h>
+#include <libxml/xmlerror.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xmlmemory.h>
+
+#include <opensrf/osrf_json.h>
+#include <opensrf/utils.h>
+#include <opensrf/osrf_list.h>
+
+
+/**
+ *     Generates an XML representation of a JSON object */
+char* jsonObjectToXML(jsonObject*);
+
+
+/*
+ * Builds a JSON object from the provided XML 
+ */
+jsonObject* jsonXMLToJSONObject(const char* xml);
+
+#endif
diff --git a/include/opensrf/osrf_legacy_json.h b/include/opensrf/osrf_legacy_json.h
new file mode 100644 (file)
index 0000000..a79fbbe
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+Copyright (C) 2005  Georgia Public Library Service 
+Bill Erickson <highfalutin@gmail.com>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+*/
+
+
+
+
+/* ---------------------------------------------------------------------------------------
+       JSON parser.
+ * --------------------------------------------------------------------------------------- */
+#ifndef LEGACY_JSON_H
+#define LEGACY_JSON_H
+
+#include <opensrf/osrf_json.h>
+#include <ctype.h>
+
+
+
+/* Parses the given JSON string and returns the built object. 
+ *     returns NULL (and prints parser error to stderr) on error.  
+ */
+
+jsonObject* json_parse_string(char* string);
+
+jsonObject* legacy_jsonParseString(char* string);
+jsonObject* legacy_jsonParseStringFmt( char* string, ... );
+
+jsonObject* json_parse_file( const char* filename );
+
+jsonObject* legacy_jsonParseFile( const char* string );
+
+
+
+/* does the actual parsing work.  returns 0 on success.  -1 on error and
+ * -2 if there was no object to build (string was all comments) 
+ */
+int _json_parse_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
+
+/* returns 0 on success and turns obj into a string object */
+int json_parse_json_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
+
+/* returns 0 on success and turns obj into a number or double object */
+int json_parse_json_number(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
+
+/* returns 0 on success and turns obj into an 'object' object */
+int json_parse_json_object(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
+
+/* returns 0 on success and turns object into an array object */
+int json_parse_json_array(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
+
+/* churns through whitespace and increments index as it goes.
+ * eat_all == true means we should eat newlines, tabs
+ */
+void json_eat_ws(char* string, unsigned long* index, int eat_all, int current_strlen);
+
+int json_parse_json_bool(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
+
+/* removes comments from a json string.  if the comment contains a class hint
+ * and class_hint isn't NULL, an allocated char* with the class name will be
+ * shoved into *class_hint.  returns 0 on success, -1 on parse error.
+ * 'index' is assumed to be at the second character (*) of the comment
+ */
+int json_eat_comment(char* string, unsigned long* index, char** class_hint, int parse_class, int current_strlen);
+
+/* prints a useful error message to stderr. always returns -1 */
+int json_handle_error(char* string, unsigned long* index, char* err_msg);
+
+int json_parse_json_null(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
+
+
+char* legacy_jsonObjectToJSON( const jsonObject* obj );
+
+
+
+/* LEGACY ITERATOR CODE ---------------------------------------------------  
+   ------------------------------------------------------------------------ */
+
+struct _jsonObjectNodeStruct {
+       unsigned long index; /* our array position */
+       char* key; /* our hash key */
+       jsonObject* item; /* our object */
+};
+typedef struct _jsonObjectNodeStruct jsonObjectNode;
+
+
+
+/* utility object for iterating over hash objects */
+struct _jsonObjectIteratorStruct {
+    jsonIterator* iterator;
+       const jsonObject* obj; /* the topic object */
+       jsonObjectNode* current; /* the current node within the object */
+    int done;
+};
+typedef struct _jsonObjectIteratorStruct jsonObjectIterator;
+
+
+/** Allocates a new iterator 
+       @param obj The object over which to iterate.
+*/
+jsonObjectIterator* jsonNewObjectIterator(const jsonObject* obj);
+
+/** 
+       De-allocates an iterator 
+       @param iter The iterator object to free
+*/
+void jsonObjectIteratorFree(jsonObjectIterator* iter);
+
+/** 
+       Returns the object_node currently pointed to by the iterator
+       and increments the pointer to the next node
+       @param iter The iterator in question.
+ */
+jsonObjectNode* jsonObjectIteratorNext(jsonObjectIterator* iter);
+
+/** 
+       @param iter The iterator.
+       @return True if there is another node after the current node.
+ */
+int jsonObjectIteratorHasNext(const jsonObjectIterator* iter);
+
+
+#endif
+
+
index 12e14c6..ca79082 100644 (file)
@@ -1,8 +1,7 @@
 #include <opensrf/string_array.h>
 #include <opensrf/utils.h>
 #include <opensrf/log.h>
-#include <objson/object.h>
-#include <objson/json_parser.h>
+#include <opensrf/osrf_json.h>
 
 
 /* libxml stuff for the config reader */
index 9aa0d47..d0e23df 100644 (file)
@@ -12,8 +12,7 @@
 #include <opensrf/utils.h>
 #include <opensrf/osrf_app_session.h>
 
-#include <objson/object.h>
-#include <objson/json_parser.h>
+#include <opensrf/osrf_json.h>
 
 typedef struct { 
        char* hostname; 
index 77b3de0..70b0038 100644 (file)
@@ -2,17 +2,20 @@
 
 #include <opensrf/utils.h>
 #include <opensrf/log.h>
+#include <opensrf/osrf_list.h>
 
-#define STRING_ARRAY_MAX_SIZE 1024
+#define STRING_ARRAY_MAX_SIZE 4096
 
 #ifndef STRING_ARRAY_H
 #define STRING_ARRAY_H
 
+#define OSRF_STRING_ARRAY_FREE(arr)\
+    if(arr) {osrfListFree(arr->list); free(arr);}
+        
+
 struct string_array_struct {
-               char** array;   
-               int size;
-               int arr_size;
-               int total_string_size;
+    osrfList* list;
+    int size;
 };
 typedef struct string_array_struct string_array;
 typedef struct string_array_struct osrfStringArray;
index 7bbade0..c6832cd 100644 (file)
@@ -64,6 +64,10 @@ GNU General Public License for more details.
                }\
        }while(0)
 
+#define OSRF_BUFFER_RESET(gb) \
+    memset(gb->buf, 0, gb->size);\
+    gb->n_used = 0;
+
        
 
 
index 2b3a030..e0321a4 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef _XML_UTILS_H
 #define _XML_UTILS_H
 
-#include <objson/object.h>
+#include <opensrf/osrf_json.h>
 #include <libxml/parser.h>
 #include <libxml/tree.h>
 
index 35911d6..667f634 100644 (file)
@@ -12,3 +12,4 @@ export APXS2=/usr/bin/apxs2
 export APACHE2_HEADERS=/usr/include/apache2
 export APR_HEADERS=/usr/include/apr-1.0/
 export LIBXML2_HEADERS=/usr/include/libxml2/
+export OSRF_LEGACY_JSON=1
index 14823ab..d377724 100644 (file)
@@ -1,17 +1,20 @@
 # TOP level 'src' makefile for OpenSRF
 
 
-export TMPDIR                  = $(TMP)/opensrf
-export OPENSRF                 = opensrf
-export BINDIR                  = $(PREFIX)/bin
-export LIBDIR                  = $(PREFIX)/lib
-export PERLDIR                 = $(LIBDIR)/perl5
-export INCLUDEDIR              = $(PREFIX)/include
-
-export LDLIBS                  += 
-export LDFLAGS                 += -Wl,-rpath=$(LIBDIR) -L $(TMPDIR) -L .
-export CFLAGS                  += -pipe -g -Wall -O2 -fPIC -I ../../include/ -I$(LIBXML2_HEADERS) -I$(APACHE2_HEADERS) \
-                                                               -I$(LIBXML2_HEADERS)/libxml -I$(APR_HEADERS) 
+export TMPDIR  = $(TMP)/opensrf
+export OPENSRF = opensrf
+export BINDIR  = $(PREFIX)/bin
+export LIBDIR  = $(PREFIX)/lib
+export PERLDIR = $(LIBDIR)/perl5
+export INCLUDEDIR= $(PREFIX)/include
+
+export LDLIBS  += 
+export LDFLAGS += -Wl,-rpath=$(LIBDIR) -L $(TMPDIR) -L .
+export CFLAGS  += -pipe -g -Wall -O2 -fPIC -I ../../include/ -I$(LIBXML2_HEADERS) -I$(APACHE2_HEADERS) -I$(APR_HEADERS) 
+
+ifeq ($(OSRF_LEGACY_JSON), 1)
+export LDLIBS += -lobjson
+endif
 
 all:   prep \
        opensrf \
@@ -26,8 +29,7 @@ install:      install-prep \
                router-install \
                srfsh-install \
                jserver-install \
-               perl-install \
-               objson-install
+               perl-install 
 
 
 # --------------------------------------------------------------------------------
@@ -37,11 +39,7 @@ install:     install-prep \
 prep:
        mkdir -p $(TMPDIR)
 
-objson/libobjson.so:   prep
-       @echo $@
-       make -C objson
-
-opensrf:       objson/libobjson.so
+opensrf:       prep
        make -C libopensrf
        make -C c-apps
 
@@ -75,12 +73,8 @@ install-prep:
        mkdir -p $(INCLUDEDIR)
        mkdir -p $(ETCDIR)
 
-objson-install:        install-prep 
-       @echo $@
-       make -C objson install
-
 # installs libopensrf.so, opensrf-c, headers, example configs, and osrf_ctl.sh
-opensrf-install:       objson-install
+opensrf-install:       install-prep
        @echo $@
        cp $(TMPDIR)/libopensrf.so $(LIBDIR)/libopensrf.so
        cp -r ../include/opensrf $(INCLUDEDIR)
@@ -90,6 +84,10 @@ opensrf-install:     objson-install
        cp ../examples/opensrf.xml.example $(ETCDIR)
        cp ../examples/opensrf_core.xml.example $(ETCDIR)
        cp ../examples/srfsh.xml.example $(ETCDIR)
+       if [ ! -z "$(OSRF_LEGACY_JSON)" ]; then\
+               cp -r ../include/objson $(INCLUDEDIR);\
+               cp $(TMPDIR)/libobjson.so $(LIBDIR);\
+       fi
 
 gateway-install:       install-prep opensrf-install    
        @echo $@
@@ -120,7 +118,6 @@ clean:
        make -C router clean
        make -C gateway clean
        make -C jserver clean
-       make -C objson clean
        make -C srfsh clean
        make -C c-apps clean
        /bin/rm -rf $(TMPDIR) *.o
index ceda792..7e99ef1 100644 (file)
@@ -1,4 +1,4 @@
-LDLIBS += -lobjson -lopensrf
+LDLIBS += -lopensrf
 CFLAGS += -DOSRF_LOG_PARAMS
 
 all:   osrf_math.so osrf_dbmath.so osrf_version.so
index 0b930cd..a88db87 100644 (file)
@@ -1,7 +1,7 @@
-#include "opensrf/osrf_app_session.h"
-#include "opensrf/osrf_application.h"
-#include "objson/object.h"
-#include "opensrf/log.h"
+#include <opensrf/osrf_app_session.h>
+#include <opensrf/osrf_application.h>
+#include <opensrf/osrf_json.h>
+#include <opensrf/log.h>
 
 #define MODULENAME "opensrf.dbmath"
 
index 63d168a..7c3cf29 100644 (file)
@@ -1,7 +1,7 @@
-#include "opensrf/osrf_app_session.h"
-#include "opensrf/osrf_application.h"
-#include "objson/object.h"
-#include "opensrf/log.h"
+#include <opensrf/osrf_app_session.h>
+#include <opensrf/osrf_application.h>
+#include <opensrf/osrf_json.h>
+#include <opensrf/log.h>
 
 #define MODULENAME "opensrf.math"
 
index 1affb70..9d07874 100644 (file)
@@ -1,6 +1,6 @@
 #include "opensrf/osrf_app_session.h"
 #include "opensrf/osrf_application.h"
-#include "objson/object.h"
+#include "opensrf/osrf_json.h"
 #include "opensrf/utils.h"
 #include "opensrf/log.h"
 
index 2120b23..3d60411 100644 (file)
@@ -1,6 +1,6 @@
 #CFLAGS        += -DASSUME_STATELESS -DOSRF_GATEWAY_NASTY_DEBUG
 CFLAGS += -DASSUME_STATELESS 
-LDLIBS += -lobjson -lopensrf
+LDLIBS += -lopensrf
 
 all: osrf_json_gateway.so copy
 
index 80bf722..9dfa5d3 100644 (file)
@@ -2,9 +2,9 @@
 #include "opensrf/osrf_app_session.h"
 #include "opensrf/osrf_system.h"
 #include "opensrf/osrfConfig.h"
-#include "objson/object.h"
-#include "objson/json2xml.h"
-#include "objson/xml2json.h"
+#include <opensrf/osrf_json.h>
+#include <opensrf/osrf_json_xml.h>
+#include <opensrf/osrf_legacy_json.h>
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <unistd.h>
@@ -13,6 +13,8 @@
 #define MODULE_NAME "osrf_json_gateway_module"
 #define GATEWAY_CONFIG "OSRFGatewayConfig"
 #define CONFIG_CONTEXT "gateway"
+#define JSON_PROTOCOL "OSRFGatewayLegacyJSON"
+#define GATEWAY_USE_LEGACY_JSON 1
 
 #define GATEWAY_DEFAULT_CONFIG "/openils/conf/opensrf_core.xml"
 
@@ -22,6 +24,11 @@ typedef struct {
        char* configfile;  /* our bootstrap config file */
 } osrf_json_gateway_config;
 
+typedef struct { 
+       int legacyJSON;
+} osrf_json_gateway_dir_config;
+
+
 module AP_MODULE_DECLARE_DATA osrf_json_gateway_module;
 
 char* osrf_json_gateway_config_file = NULL;
@@ -37,10 +44,18 @@ static const char* osrf_json_gateway_set_config(cmd_parms *parms, void *config,
        return NULL;
 }
 
+static const char* osrf_json_gateway_set_json_proto(cmd_parms *parms, void *config, const char *arg) {
+       osrf_json_gateway_dir_config* cfg = (osrf_json_gateway_dir_config*) config;
+       cfg->legacyJSON = (!strcasecmp((char*) arg, "false")) ? 0 : 1;
+       return NULL;
+}
+
 /* tell apache about our commands */
 static const command_rec osrf_json_gateway_cmds[] = {
        AP_INIT_TAKE1( GATEWAY_CONFIG, osrf_json_gateway_set_config, 
                        NULL, RSRC_CONF, "osrf json gateway config file"),
+       AP_INIT_TAKE1( JSON_PROTOCOL, osrf_json_gateway_set_json_proto,
+                       NULL, ACCESS_CONF, "osrf json gateway config file"),
        {NULL}
 };
 
@@ -52,6 +67,13 @@ static void* osrf_json_gateway_create_config( apr_pool_t* p, server_rec* s) {
        return (void*) cfg;
 }
 
+static void* osrf_json_gateway_create_dir_config( apr_pool_t* p, char* dir) {
+       osrf_json_gateway_dir_config* cfg = (osrf_json_gateway_dir_config*) 
+                       apr_palloc(p, sizeof(osrf_json_gateway_dir_config));
+       cfg->legacyJSON = GATEWAY_USE_LEGACY_JSON;
+       return (void*) cfg;
+}
+
 
 static void osrf_json_gateway_child_init(apr_pool_t *p, server_rec *s) {
 
@@ -84,6 +106,25 @@ static int osrf_json_gateway_method_handler (request_rec *r) {
        /* make sure we're needed first thing*/
        if (strcmp(r->handler, MODULE_NAME )) return DECLINED;
 
+
+       osrf_json_gateway_dir_config* dir_conf =  
+               ap_get_module_config(r->per_dir_config, &osrf_json_gateway_module);
+
+
+       /* provide 2 different JSON parsers and serializers to support legacy JSON */
+       jsonObject* (*parseJSONFunc) (char*) = legacy_jsonParseString;
+       char* (*jsonToStringFunc) (const jsonObject*) = legacy_jsonObjectToJSON;
+
+       if(dir_conf->legacyJSON) {
+           ap_log_rerror( APLOG_MARK, APLOG_INFO, 0, r, "Using legacy JSON");
+
+    } else {
+           ap_log_rerror( APLOG_MARK, APLOG_INFO, 0, r, "Not using legacy JSON");
+               parseJSONFunc = jsonParseString;
+               jsonToStringFunc = jsonObjectToJSON;
+       }
+
+
        osrfLogDebug(OSRF_LOG_MARK, "osrf gateway: entered request handler");
 
        /* verify we are connected */
@@ -169,7 +210,13 @@ static int osrf_json_gateway_method_handler (request_rec *r) {
                int req_id = -1;
 
         if(!strcasecmp(input_format, "json")) {
-                   req_id = osrf_app_session_make_req( session, NULL, method, api_level, mparams );
+            jsonObject * arr = jsonNewObject(NULL);
+            char* str;
+            int i = 0;
+            while( (str = osrfStringArrayGetString(mparams, i++)) ) 
+                jsonObjectPush(arr, parseJSONFunc(str));
+
+                   req_id = osrf_app_session_make_req( session, arr, method, api_level, NULL );
 
         } else {
 
@@ -247,7 +294,8 @@ static int osrf_json_gateway_method_handler (request_rec *r) {
                                if (isXML) {
                                        output = jsonObjectToXML( res );
                                } else {
-                                       output = jsonObjectToJSON( res );
+                                       //output = jsonObjectToJSON( res );
+                    output = jsonToStringFunc( res );
                                        if( morethan1 ) ap_rputs(",", r); /* comma between JSON array items */
                                }
                                ap_rputs(output, r);
@@ -293,7 +341,8 @@ static int osrf_json_gateway_method_handler (request_rec *r) {
                                bzero(bb, l);
                                snprintf(bb, l,  "%s : %s", statusname, statustext);
                                jsonObject* tmp = jsonNewObject(bb);
-                               char* j = jsonObjectToJSON(tmp);
+                char* j = jsonToStringFunc(tmp);
+                               //char* j = jsonObjectToJSON(tmp);
                                snprintf( buf, l, ",\"debug\": %s", j);
                                free(j);
                                jsonObjectFree(tmp);
@@ -344,7 +393,7 @@ static void osrf_json_gateway_register_hooks (apr_pool_t *p) {
 
 module AP_MODULE_DECLARE_DATA osrf_json_gateway_module = {
        STANDARD20_MODULE_STUFF,
-       NULL,
+       osrf_json_gateway_create_dir_config,
        NULL,
        osrf_json_gateway_create_config,
        NULL,
index e21cfe1..33f9ced 100644 (file)
@@ -1,4 +1,4 @@
-LDLIBS += -lopensrf -lobjson -lxml2
+LDLIBS += -lopensrf  -lxml2
 CFLAGS += -D_GNU_SOURCE
 
 all: chopchop
index dff2024..c6d5d53 100644 (file)
@@ -1,10 +1,17 @@
-# OSRF_LOG_PARAMS log all incoming method params at OSRF_INFO log level. 
-# OSRF_STRICT_PARAMS instructs the app handler to return an error if the number of method arguments
-#      provided to any method is not at least as large as the 'argc' setting for the method
+# ------------------------------------------------------------------
+# To build the standalone JSON lib libosrf_json.so:
+#      $ make libosrf_json.so
+# To build the standalone JSON lib libosrf_json.so with XML utils 
+#      support, use something like the following:
+#      $ CFLAGS="-DOSRF_JSON_ENABLE_XML_UTILS -I/usr/include/libxml2" LDLIBS=-lxml2 make libosrf_json.so
+#
+# The compiler flag -DOSRF_JSON_ALLOW_COMMENTS tells the parser to 
+# allow legacy JSON comments like /* comment */
+# ------------------------------------------------------------------
 
-CFLAGS +=  -DASSUME_STATELESS  -DOSRF_LOG_PARAMS -DOSRF_STRICT_PARAMS -rdynamic -fno-strict-aliasing
-LDLIBS += -lxml2 -lobjson -ldl -lmemcache 
-OSRF_INC = ../../include/opensrf/
+CFLAGS +=  -DASSUME_STATELESS  -DOSRF_STRICT_PARAMS -rdynamic -fno-strict-aliasing -I../../include -fPIC -Wall -DOSRF_JSON_ENABLE_XML_UTILS
+LDLIBS += -lxml2 -ldl -lmemcache 
+export OSRF_INC = ../../include/opensrf/
 
 TARGETS =      osrf_message.o \
                        osrf_app_session.o \
@@ -27,12 +34,24 @@ TARGETS =   osrf_message.o \
                        utils.o\
                        socket_bundle.o\
                        sha.o\
-                       string_array.o 
+                       string_array.o
 
+JSON_TARGETS =         osrf_json_object.o\
+                               osrf_json_parser.o \
+                               osrf_json_tools.o \
+                               osrf_legacy_json.o \
+                               osrf_json_xml.o
 
-all: opensrf
+# use these when building the standalone JSON module
+JSON_DEPS = osrf_list.o\
+                       osrf_hash.o\
+                       utils.o\
+                       log.o\
+                       md5.o\
+                       string_array.o
 
 
+all: opensrf
 
 # Build the OpenSRF C binary
 opensrf:       opensrf.o libopensrf.so
@@ -41,8 +60,26 @@ opensrf:     opensrf.o libopensrf.so
 
 
 # Build the OpenSRF library
-libopensrf.so: $(TARGETS)
-       $(CC) -shared -W1 $(LDFLAGS) $(LDLIBS) $(TARGETS) -o $(TMPDIR)/libopensrf.so
+libopensrf.so: $(TARGETS) json
+       if [ ! -z "$(OSRF_LEGACY_JSON)" ]; then\
+               $(CC) -shared -W1 $(LDFLAGS) $(LDLIBS) $(TARGETS) -o $(TMPDIR)/libopensrf.so;\
+       else\
+               $(CC) -shared -W1 $(LDFLAGS) $(LDLIBS) $(TARGETS) $(JSON_TARGETS) -o $(TMPDIR)/libopensrf.so;\
+       fi; 
+
+
+json:  $(JSON_TARGETS) $(JSON_DEPS)
+       if [ ! -z "$(OSRF_LEGACY_JSON)" ]; then \
+               $(CC) -shared -W1  $(CFLAGS) \
+                       $(LDFLAGS) $(JSON_TARGETS) $(JSON_DEPS) -o $(TMPDIR)/libobjson.so;\
+       fi;
+
+libosrf_json.so:       $(JSON_TARGETS) $(JSON_DEPS)
+       $(CC) -shared -W1 $(CFLAGS) \
+               $(LDFLAGS) $(LDLIBS) $(JSON_TARGETS) $(JSON_DEPS) -o $@
+
+
+osrf_json_test:        osrf_json_test.o $(JSON_TARGETS) $(JSON_DEPS)
 
 
 opensrf.o:     opensrf.c
@@ -66,8 +103,17 @@ utils.o:    utils.c $(OSRF_INC)/utils.h
 socket_bundle.o:       socket_bundle.c $(OSRF_INC)/socket_bundle.h
 sha.o: sha.c $(OSRF_INC)/sha.h
 string_array.o:        string_array.c $(OSRF_INC)/string_array.h
+osrf_json_object.o:    osrf_json_object.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h
+osrf_json_parser.o:    osrf_json_parser.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h
+osrf_json_tools.o:     osrf_json_tools.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h
+osrf_legacy_json.o:    osrf_legacy_json.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h
+osrf_json_xml.o:       osrf_json_xml.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_xml.h
+osrf_json_test.o:      osrf_json_test.c
 
 
 clean:
-       /bin/rm -f *.o libopensrf.so opensrf
+       /bin/rm -f *.o *.so opensrf osrf_json_test
+
+
+
 
diff --git a/src/libopensrf/Makefile.json b/src/libopensrf/Makefile.json
new file mode 100644 (file)
index 0000000..8de20a6
--- /dev/null
@@ -0,0 +1,40 @@
+#-DOSRF_JSON_ALLOW_COMMENTS 
+
+# ------------------------------------------------------------------
+# To build a standalone version of libosrf_json, something 
+# like the following should work:
+# $ CFLAGS="-fPIC -I /usr/include/libxml2 -I ../../include" \
+#      OSRF_INC="../../include/opensrf" LDLIBS="-lxml2" \
+#      make -f Makefile.json standalone
+# ------------------------------------------------------------------
+TARGETS = osrf_json_object.o osrf_json_parser.o osrf_json_tools.o osrf_legacy_json.o osrf_json_xml.o
+
+# these are only needed when compiling the standalone version
+EXT_TARGETS = osrf_list.o osrf_hash.o utils.o log.o md5.o string_array.o
+
+all:   $(TARGETS)
+       if [ ! -z "$(OSRF_LEGACY_JSON)" ]; then \
+               $(CC) -shared -W1 $(LDFLAGS) $(TARGETS) -o $(TMPDIR)/libobjson.so;\
+       fi;
+
+standalone: $(TARGETS) $(EXT_TARGETS)
+       $(CC) -shared -W1 $(CFLAGS) $(LDFLAGS) $(LDLIBS) $(TARGETS) $(EXT_TARGETS) -o libosrf_json.so
+
+osrf_json_object.o:    osrf_json_object.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h
+osrf_json_parser.o:    osrf_json_parser.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h
+osrf_json_tools.o:     osrf_json_tools.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h
+osrf_legacy_json.o:    osrf_legacy_json.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_utils.h
+osrf_json_xml.o:       osrf_json_xml.c $(OSRF_INC)/osrf_json.h $(OSRF_INC)/osrf_json_xml.h
+
+
+osrf_list.o:   osrf_list.c $(OSRF_INC)/osrf_list.h
+osrf_hash.o:   osrf_hash.c $(OSRF_INC)/osrf_hash.h
+utils.o:       utils.c $(OSRF_INC)/utils.h
+md5.o: md5.c $(OSRF_INC)/md5.h
+log.o: log.c $(OSRF_INC)/log.h
+string_array.o:        string_array.c $(OSRF_INC)/string_array.h
+
+
+clean:
+       rm -f osrf_json*.o osrf_legacy_json.o libosrf_json.so
+
index 1f12b86..b69659f 100644 (file)
@@ -1,11 +1,7 @@
 #include <opensrf/osrf_application.h>
-#include <objson/object.h>
-
-//osrfApplication* __osrfAppList = NULL; 
 
 osrfHash* __osrfAppHash = NULL;
 
-
 int osrfAppRegisterApplication( char* appName, char* soFile ) {
        if(!appName || ! soFile) return -1;
        char* error;
index e00f09c..5447466 100644 (file)
@@ -193,7 +193,7 @@ void osrfHashFree( osrfHash* hash ) {
        }
 
        osrfListFree(hash->hash);
-       osrfStringArrayFree(hash->keys);
+    OSRF_STRING_ARRAY_FREE(hash->keys);
        free(hash);
 }
 
@@ -201,7 +201,6 @@ void osrfHashFree( osrfHash* hash ) {
 
 osrfHashIterator* osrfNewHashIterator( osrfHash* hash ) {
        if(!hash) return NULL;
-       //osrfHashIterator* itr = safe_malloc(sizeof(osrfHashIterator));
        osrfHashIterator* itr;
        OSRF_MALLOC(itr, sizeof(osrfHashIterator));
        itr->hash = hash;
@@ -214,8 +213,7 @@ void* osrfHashIteratorNext( osrfHashIterator* itr ) {
        if(!(itr && itr->hash)) return NULL;
        if( itr->currentIdx >= itr->keys->size ) return NULL;
        free(itr->current);
-       itr->current = strdup(
-                       osrfStringArrayGetString(itr->keys, itr->currentIdx++));
+       itr->current = strdup(osrfStringArrayGetString(itr->keys, itr->currentIdx++));
        char* val = osrfHashGet( itr->hash, itr->current );
        return val;
 }
@@ -223,14 +221,12 @@ void* osrfHashIteratorNext( osrfHashIterator* itr ) {
 void osrfHashIteratorFree( osrfHashIterator* itr ) {
        if(!itr) return;
        free(itr->current);
-       //osrfStringArrayFree(itr->keys);
        free(itr);
 }
 
 void osrfHashIteratorReset( osrfHashIterator* itr ) {
        if(!itr) return;
        free(itr->current);
-       //osrfStringArrayFree(itr->keys);
        itr->keys = osrfHashKeysInc(itr->hash);
        itr->currentIdx = 0;
        itr->current = NULL;
diff --git a/src/libopensrf/osrf_json_object.c b/src/libopensrf/osrf_json_object.c
new file mode 100644 (file)
index 0000000..0b1ffd4
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+Copyright (C) 2006  Georgia Public Library Service 
+Bill Erickson <billserickson@gmail.com>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+*/
+
+#include <opensrf/osrf_json.h>
+#include <opensrf/osrf_json_utils.h>
+
+jsonObject* jsonNewObject(const char* data) {
+
+       jsonObject* o;
+       OSRF_MALLOC(o, sizeof(jsonObject));
+       o->type = JSON_NULL;
+
+       if(data) {
+               o->type = JSON_STRING;
+               o->value.s = strdup(data);
+       }
+
+       return o;
+}
+
+jsonObject* jsonNewObjectFmt(const char* data, ...) {
+
+       jsonObject* o;
+       OSRF_MALLOC(o, sizeof(jsonObject));
+       o->type = JSON_NULL;
+
+       if(data) {
+               VA_LIST_TO_STRING(data);
+               o->type = JSON_STRING;
+               o->value.s = strdup(VA_BUF);
+       }
+
+       return o;
+}
+
+jsonObject* jsonNewNumberObject( double num ) {
+       jsonObject* o = jsonNewObject(NULL);
+       o->type = JSON_NUMBER;
+       o->value.n = num;
+       return o;
+}
+
+jsonObject* jsonNewBoolObject(int val) {
+    jsonObject* o = jsonNewObject(NULL);
+    o->type = JSON_BOOL;
+    jsonSetBool(o, val);
+    return o;
+}
+
+jsonObject* jsonNewObjectType(int type) {
+       jsonObject* o = jsonNewObject(NULL);
+       o->type = type;
+       return o;
+}
+
+void jsonObjectFree( jsonObject* o ) {
+
+       if(!o || o->parent) return;
+       free(o->classname);
+
+       switch(o->type) {
+               case JSON_HASH          : osrfHashFree(o->value.h); break;
+               case JSON_ARRAY : osrfListFree(o->value.l); break;
+               case JSON_STRING        : free(o->value.s); break;
+       }
+       free(o);
+}
+
+static void _jsonFreeHashItem(char* key, void* item){
+       if(!item) return;
+       jsonObject* o = (jsonObject*) item;
+       o->parent = NULL; /* detach the item */
+       jsonObjectFree(o);
+}
+static void _jsonFreeListItem(void* item){
+       if(!item) return;
+       jsonObject* o = (jsonObject*) item;
+       o->parent = NULL; /* detach the item */
+       jsonObjectFree(o);
+}
+
+void jsonSetBool(jsonObject* bl, int val) {
+    if(!bl) return;
+    JSON_INIT_CLEAR(bl, JSON_BOOL);
+    bl->value.b = val;
+}
+
+unsigned long jsonObjectPush(jsonObject* o, jsonObject* newo) {
+    if(!o) return -1;
+    if(!newo) newo = jsonNewObject(NULL);
+       JSON_INIT_CLEAR(o, JSON_ARRAY);
+       newo->parent = o;
+       osrfListPush( o->value.l, newo );
+       o->size = o->value.l->size;
+       return o->size;
+}
+
+unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj) {
+    if(!dest) return -1;
+    if(!newObj) newObj = jsonNewObject(NULL);
+       JSON_INIT_CLEAR(dest, JSON_ARRAY);
+       newObj->parent = dest;
+       osrfListSet( dest->value.l, newObj, index );
+       dest->size = dest->value.l->size;
+       return dest->value.l->size;
+}
+
+unsigned long jsonObjectSetKey( jsonObject* o, const char* key, jsonObject* newo) {
+    if(!o) return -1;
+    if(!newo) newo = jsonNewObject(NULL);
+       JSON_INIT_CLEAR(o, JSON_HASH);
+       newo->parent = o;
+       osrfHashSet( o->value.h, newo, key );
+       o->size = o->value.h->size;
+       return o->size;
+}
+
+jsonObject* jsonObjectGetKey( const jsonObject* obj, const char* key ) {
+       if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
+       return osrfHashGet( obj->value.h, key);
+}
+
+char* jsonObjectToJSON( const jsonObject* obj ) {
+       jsonObject* obj2 = jsonObjectEncodeClass( (jsonObject*) obj);
+       char* json = jsonObjectToJSONRaw(obj2);
+       jsonObjectFree(obj2);
+       return json;
+}
+
+char* jsonObjectToJSONRaw( const jsonObject* obj ) {
+       if(!obj) return NULL;
+       growing_buffer* buf = buffer_init(32);
+       int i;
+    char* json;
+
+       switch(obj->type) {
+
+               case JSON_BOOL :
+                       if(obj->value.b) OSRF_BUFFER_ADD(buf, "true"); 
+                       else OSRF_BUFFER_ADD(buf, "false"); 
+                       break;
+
+               case JSON_NUMBER: {
+                       double x = obj->value.n;
+                       if( x == (int) x ) {
+                               INT_TO_STRING((int)x);  
+                               OSRF_BUFFER_ADD(buf, INTSTR);
+
+                       } else {
+                               DOUBLE_TO_STRING(x);
+                               OSRF_BUFFER_ADD(buf, DOUBLESTR);
+                       }
+                       break;
+               }
+
+               case JSON_NULL:
+                       OSRF_BUFFER_ADD(buf, "null");
+                       break;
+
+               case JSON_STRING:
+                       OSRF_BUFFER_ADD_CHAR(buf, '"');
+                       char* data = obj->value.s;
+                       int len = strlen(data);
+                       
+                       char* output = uescape(data, len, 1);
+                       OSRF_BUFFER_ADD(buf, output);
+                       free(output);
+                       OSRF_BUFFER_ADD_CHAR(buf, '"');
+                       break;
+                       
+               case JSON_ARRAY: {
+                       OSRF_BUFFER_ADD_CHAR(buf, '[');
+                       if( obj->value.l ) {
+                               for( i = 0; i != obj->value.l->size; i++ ) {
+                                       json = jsonObjectToJSONRaw(OSRF_LIST_GET_INDEX(obj->value.l, i));
+                                       if(i > 0) OSRF_BUFFER_ADD(buf, ",");
+                                       OSRF_BUFFER_ADD(buf, json);
+                                       free(json);
+                               }
+                       } 
+                       OSRF_BUFFER_ADD_CHAR(buf, ']');
+                       break;
+               }
+
+               case JSON_HASH: {
+
+                       OSRF_BUFFER_ADD_CHAR(buf, '{');
+                       osrfHashIterator* itr = osrfNewHashIterator(obj->value.h);
+                       jsonObject* item;
+                       int i = 0;
+
+                       while( (item = osrfHashIteratorNext(itr)) ) {
+                               if(i++ > 0) OSRF_BUFFER_ADD(buf, ",");
+                               buffer_fadd(buf, "\"%s\":", itr->current);
+                               char* json = jsonObjectToJSONRaw(item);
+                               OSRF_BUFFER_ADD(buf, json);
+                               free(json);
+                       }
+
+                       osrfHashIteratorFree(itr);
+                       OSRF_BUFFER_ADD_CHAR(buf, '}');
+                       break;
+               }
+       }
+
+    return buffer_release(buf);
+}
+
+
+jsonIterator* jsonNewIterator(const jsonObject* obj) {
+       if(!obj) return NULL;
+       jsonIterator* itr;
+       OSRF_MALLOC(itr, sizeof(jsonIterator));
+
+       itr->obj                = (jsonObject*) obj;
+       itr->index      = 0;
+       itr->key                = NULL;
+
+       if( obj->type == JSON_HASH )
+               itr->hashItr = osrfNewHashIterator(obj->value.h);
+       
+       return itr;
+}
+
+void jsonIteratorFree(jsonIterator* itr) {
+       if(!itr) return;
+       free(itr->key);
+       osrfHashIteratorFree(itr->hashItr);
+       free(itr);
+}
+
+jsonObject* jsonIteratorNext(jsonIterator* itr) {
+       if(!(itr && itr->obj)) return NULL;
+       if( itr->obj->type == JSON_HASH ) {
+               if(!itr->hashItr) return NULL;
+               jsonObject* item = osrfHashIteratorNext(itr->hashItr);
+               free(itr->key);
+               itr->key = strdup(itr->hashItr->current);
+               return item;
+       } else {
+               return jsonObjectGetIndex( itr->obj, itr->index++ );
+       }
+}
+
+int jsonIteratorHasNext(const jsonIterator* itr) {
+       if(!(itr && itr->obj)) return 0;
+       if( itr->obj->type == JSON_HASH )
+               return osrfHashIteratorHasNext( itr->hashItr );
+       return (itr->index < itr->obj->size) ? 1 : 0;
+}
+
+jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index ) {
+       if(!obj) return NULL;
+       return (obj->type == JSON_ARRAY) ? 
+        (OSRF_LIST_GET_INDEX(obj->value.l, index)) : NULL;
+}
+
+
+
+unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index) {
+       if( dest && dest->type == JSON_ARRAY ) {
+               osrfListRemove(dest->value.l, index);
+               return dest->value.l->size;
+       }
+       return -1;
+}
+
+
+unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key) {
+       if( dest && key && dest->type == JSON_HASH ) {
+               osrfHashRemove(dest->value.h, key);
+               return 1;
+       }
+       return -1;
+}
+
+char* jsonObjectGetString(const jsonObject* obj) {
+       return (obj && obj->type == JSON_STRING) ? obj->value.s : NULL;
+}
+
+double jsonObjectGetNumber( const jsonObject* obj ) {
+       return (obj && obj->type == JSON_NUMBER) ? obj->value.n : 0;
+}
+
+void jsonObjectSetString(jsonObject* dest, const char* string) {
+       if(!(dest && string)) return;
+       JSON_INIT_CLEAR(dest, JSON_STRING);
+       free(dest->value.s);
+       dest->value.s = strdup(string);
+}
+
+void jsonObjectSetNumber(jsonObject* dest, double num) {
+       if(!dest) return;
+       JSON_INIT_CLEAR(dest, JSON_NUMBER);
+       dest->value.n = num;
+}
+
+void jsonObjectSetClass(jsonObject* dest, const char* classname ) {
+       if(!(dest && classname)) return;
+       free(dest->classname);
+       dest->classname = strdup(classname);
+}
+const char* jsonObjectGetClass(const jsonObject* dest) {
+    if(!dest) return NULL;
+    return dest->classname;
+}
+
+jsonObject* jsonObjectClone( const jsonObject* o ) {
+    if(!o) return jsonNewObject(NULL);
+
+    int i;
+    jsonObject* arr; 
+    jsonObject* hash; 
+    jsonIterator* itr;
+    jsonObject* tmp;
+    jsonObject* result = NULL;
+
+    switch(o->type) {
+        case JSON_NULL:
+            result = jsonNewObject(NULL);
+            break;
+        case JSON_STRING:
+            result = jsonNewObject(jsonObjectGetString(o));
+            break;
+        case JSON_NUMBER:
+            result = jsonNewNumberObject(jsonObjectGetNumber(o));
+            break;
+        case JSON_BOOL:
+            result = jsonNewBoolObject(jsonBoolIsTrue((jsonObject*) o));
+            break;
+        case JSON_ARRAY:
+            arr = jsonNewObject(NULL);
+            arr->type = JSON_ARRAY;
+            for(i=0; i < o->size; i++) 
+                jsonObjectPush(arr, jsonObjectClone(jsonObjectGetIndex(o, i)));
+            result = arr;
+            break;
+        case JSON_HASH:
+            hash = jsonNewObject(NULL);
+            hash->type = JSON_HASH;
+            itr = jsonNewIterator(o);
+            while( (tmp = jsonIteratorNext(itr)) )
+                jsonObjectSetKey(hash, itr->key, jsonObjectClone(tmp));
+            jsonIteratorFree(itr);
+            result = hash;
+            break;
+    }
+
+    jsonObjectSetClass(result, jsonObjectGetClass(o));
+    return result;
+}
+
+int jsonBoolIsTrue( jsonObject* boolObj ) {
+    if( boolObj && boolObj->type == JSON_BOOL && boolObj->value.b )
+        return 1;
+    return 0;
+}
+
+
+char* jsonObjectToSimpleString( const jsonObject* o ) {
+       if(!o) return NULL;
+
+       char* value = NULL;
+
+       switch( o->type ) {
+
+               case JSON_NUMBER: {
+
+                       if( o->value.n == (int) o->value.n ) {
+                               INT_TO_STRING((int) o->value.n);        
+                               value = strdup(INTSTR);
+
+                       } else {
+                               DOUBLE_TO_STRING(o->value.n);
+                               value = strdup(DOUBLESTR);
+                       }
+
+                       break;
+               }
+
+               case JSON_STRING:
+                       value = strdup(o->value.s);
+       }
+
+       return value;
+}
+
+
diff --git a/src/libopensrf/osrf_json_parser.c b/src/libopensrf/osrf_json_parser.c
new file mode 100644 (file)
index 0000000..0837652
--- /dev/null
@@ -0,0 +1,632 @@
+/*
+Copyright (C) 2006  Georgia Public Library Service 
+Bill Erickson <billserickson@gmail.com>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+*/
+
+#include <opensrf/osrf_json.h>
+#include <opensrf/osrf_json_utils.h>
+#include <ctype.h>
+
+
+/* if the client sets a global error handler, this will point to it */
+static void (*jsonClientErrorCallback) (const char*) = NULL;
+
+/* these are the handlers for our internal parser */
+static jsonParserHandler jsonInternalParserHandlerStruct = {
+       _jsonHandleStartObject,
+       _jsonHandleObjectKey,
+       _jsonHandleEndObject,
+       _jsonHandleStartArray,
+       _jsonHandleEndArray,
+       _jsonHandleNull,
+       _jsonHandleString,
+       _jsonHandleBool,
+       _jsonHandleNumber,
+       _jsonHandleError
+};
+static jsonParserHandler* 
+       jsonInternalParserHandler = &jsonInternalParserHandlerStruct; 
+
+
+jsonParserContext* jsonNewParser( jsonParserHandler* handler, void* userData) {
+       jsonParserContext* ctx;
+       OSRF_MALLOC(ctx, sizeof(jsonParserContext));
+       ctx->stateStack                 = osrfNewList();
+       ctx->buffer                                     = buffer_init(512);
+       ctx->utfbuf                                     = buffer_init(5);
+       ctx->handler                            = handler;
+       ctx->state                                      = 0;
+       ctx->index                                      = 0;
+       ctx->chunk                                      = NULL;
+       ctx->userData                           = userData;
+       return ctx;
+}
+
+void jsonParserFree( jsonParserContext* ctx ) {
+       if(!ctx) return;
+       buffer_free(ctx->buffer);
+       buffer_free(ctx->utfbuf);
+       osrfListFree(ctx->stateStack);
+       free(ctx);
+}
+
+
+void jsonSetGlobalErrorHandler(void (*errorHandler) (const char*)) {
+       jsonClientErrorCallback = errorHandler;
+}
+
+
+int _jsonParserError( jsonParserContext* ctx, char* err, ... ) {
+       if( ctx->handler->handleError ) {
+               VA_LIST_TO_STRING(err);
+               int pre = ctx->index - 15;
+               int post= ctx->index + 15;
+               while( pre < 0 ) pre++;
+               while( post >= ctx->chunksize ) post--;
+               int l = post - pre;
+               char buf[l];
+        memset(buf, 0, l);
+               snprintf(buf, l, ctx->chunk + pre);
+               ctx->handler->handleError( ctx->userData, 
+                       "*JSON Parser Error\n - char  = %c\n "
+                       "- index = %d\n - near  => %s\n - %s", 
+                       ctx->chunk[ctx->index], ctx->index, buf, VA_BUF );
+       }
+       JSON_STATE_SET(ctx, JSON_STATE_IS_INVALID);
+       return -1;
+}
+
+
+int _jsonParserHandleUnicode( jsonParserContext* ctx ) {
+
+       /* collect as many of the utf characters as we can in this chunk */
+       JSON_CACHE_DATA(ctx, ctx->utfbuf, 4);
+
+       /* we ran off the end of the chunk */
+       if( ctx->utfbuf->n_used < 4 ) {
+               JSON_STATE_SET(ctx, JSON_STATE_IN_UTF);
+               return 1;
+       }
+
+       ctx->index--; /* push it back to index of the final utf char */
+
+       /* ----------------------------------------------------------------------- */
+       /* We have all of the escaped unicode data.  Write it to the buffer */
+       /* The following chunk is used with permission from 
+        * json-c http://oss.metaparadigm.com/json-c/ 
+        */
+       #define hexdigit(x) ( ((x) <= '9') ? (x) - '0' : ((x) & 7) + 9)
+       unsigned char utf_out[4];
+       memset(utf_out,0,4);
+       char* buf = ctx->utfbuf->buf;
+
+       unsigned int ucs_char =
+               (hexdigit(buf[0] ) << 12) +
+               (hexdigit(buf[1]) << 8) +
+               (hexdigit(buf[2]) << 4) +
+               hexdigit(buf[3]);
+
+       if (ucs_char < 0x80) {
+               utf_out[0] = ucs_char;
+               OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out);
+
+       } else if (ucs_char < 0x800) {
+               utf_out[0] = 0xc0 | (ucs_char >> 6);
+               utf_out[1] = 0x80 | (ucs_char & 0x3f);
+               OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out);
+
+       } else {
+               utf_out[0] = 0xe0 | (ucs_char >> 12);
+               utf_out[1] = 0x80 | ((ucs_char >> 6) & 0x3f);
+               utf_out[2] = 0x80 | (ucs_char & 0x3f);
+               OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out);
+       }
+       /* ----------------------------------------------------------------------- */
+       /* ----------------------------------------------------------------------- */
+
+       JSON_STATE_REMOVE(ctx, JSON_STATE_IN_UTF);
+       JSON_STATE_REMOVE(ctx, JSON_STATE_IN_ESCAPE);
+       OSRF_BUFFER_RESET(ctx->utfbuf);
+       return 0;
+}
+
+
+
+/* type : 0=null, 1=true, 2=false */
+int _jsonParserHandleMatch( jsonParserContext* ctx, int type ) {
+
+       switch(type) {
+
+               case 0: /* JSON null */
+
+                       /* first see if we have it all first */
+                       if( ctx->chunksize > (ctx->index + 3) ) {
+                               if( strncasecmp(ctx->chunk + ctx->index, "null", 4) ) 
+                                       return _jsonParserError(ctx, "Invalid JSON 'null' sequence");
+                               if( ctx->handler->handleNull ) 
+                                       ctx->handler->handleNull(ctx->userData);
+                               ctx->index += 4;
+                               break;
+                       }
+
+                       JSON_CACHE_DATA(ctx, ctx->buffer, 4);
+                       if( ctx->buffer->n_used < 4 ) {
+                               JSON_STATE_SET(ctx, JSON_STATE_IN_NULL);
+                               return 1;
+                       } 
+
+                       if( strncasecmp(ctx->buffer->buf, "null", 4) ) 
+                               return _jsonParserError(ctx, "Invalid JSON 'null' sequence");
+                       if( ctx->handler->handleNull ) 
+                               ctx->handler->handleNull(ctx->userData);
+                       break;
+
+               case 1: /* JSON true */
+
+                       /* see if we have it all first */
+                       if( ctx->chunksize > (ctx->index + 3) ) {
+                               if( strncasecmp(ctx->chunk + ctx->index, "true", 4) ) 
+                                       return _jsonParserError(ctx, "Invalid JSON 'true' sequence");
+                               if( ctx->handler->handleBool ) 
+                                       ctx->handler->handleBool(ctx->userData, 1);
+                               ctx->index += 4;
+                               break;
+                       }
+
+                       JSON_CACHE_DATA(ctx, ctx->buffer, 4);
+                       if( ctx->buffer->n_used < 4 ) {
+                               JSON_STATE_SET(ctx, JSON_STATE_IN_TRUE);
+                               return 1;
+                       } 
+                       if( strncasecmp( ctx->buffer->buf, "true", 4 ) ) {
+                               return _jsonParserError(ctx, "Invalid JSON 'true' sequence");
+                       }
+                       if( ctx->handler->handleBool ) 
+                               ctx->handler->handleBool(ctx->userData, 1);
+                       break;
+
+               case 2: /* JSON false */
+
+                       /* see if we have it all first */
+                       if( ctx->chunksize > (ctx->index + 4) ) {
+                               if( strncasecmp(ctx->chunk + ctx->index, "false", 5) ) 
+                                       return _jsonParserError(ctx, "Invalid JSON 'false' sequence");
+                               if( ctx->handler->handleBool ) 
+                                       ctx->handler->handleBool(ctx->userData, 0);
+                               ctx->index += 5;
+                               break;
+                       }
+
+                       JSON_CACHE_DATA(ctx, ctx->buffer, 5);
+                       if( ctx->buffer->n_used < 5 ) {
+                               JSON_STATE_SET(ctx, JSON_STATE_IN_FALSE);
+                               return 1;
+                       }
+                       if( strncasecmp( ctx->buffer->buf, "false", 5 ) ) 
+                               return _jsonParserError(ctx, "Invalid JSON 'false' sequence");
+                       if( ctx->handler->handleBool ) 
+                               ctx->handler->handleBool(ctx->userData, 0);
+                       break;
+
+               default: 
+                       fprintf(stderr, "Invalid type flag\n");
+                       return -1;
+
+       }
+
+       ctx->index--; /* set it back to the index of the final sequence character */
+       OSRF_BUFFER_RESET(ctx->buffer);
+       JSON_STATE_REMOVE(ctx, JSON_STATE_IN_NULL);
+       JSON_STATE_REMOVE(ctx, JSON_STATE_IN_TRUE);
+       JSON_STATE_REMOVE(ctx, JSON_STATE_IN_FALSE);
+
+       return 0;
+}
+
+
+int _jsonParserHandleString( jsonParserContext* ctx ) {
+
+       char c = ctx->chunk[ctx->index];
+
+       if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_ESCAPE) ) {
+
+               if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_UTF) ) {
+
+                       return _jsonParserHandleUnicode( ctx );
+                                               
+               } else {
+
+                       switch(c) {
+
+                               /* handle all of the escape chars */
+                               case '\\': OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\\' ); break;
+                               case '"'        : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\"' ); break;
+                               case 't'        : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\t' ); break;
+                               case 'b'        : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\b' ); break;
+                               case 'f'        : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\f' ); break;
+                               case 'r'        : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\r' ); break;
+                               case 'n'        : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\n' ); break;
+                               case 'u'        : 
+                                       ctx->index++; /* progress to the first utf char */
+                                       return _jsonParserHandleUnicode( ctx );
+                               default : OSRF_BUFFER_ADD_CHAR( ctx->buffer, c );
+                       }
+               }
+
+               JSON_STATE_REMOVE(ctx, JSON_STATE_IN_ESCAPE);
+               return 0;
+
+       } else {
+
+               switch(c) {
+
+                       case '"'        : /* this string is ending */
+                               if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_KEY) ) {
+
+                                       /* object key */
+                                       if(ctx->handler->handleObjectKey) {
+                                               ctx->handler->handleObjectKey( 
+                                                       ctx->userData, ctx->buffer->buf);
+                                       }
+
+                               } else { /* regular json string */
+
+                                       if(ctx->handler->handleString) {
+                                               ctx->handler->handleString( 
+                                                       ctx->userData, ctx->buffer->buf );
+                                       }
+
+                               }
+
+                               OSRF_BUFFER_RESET(ctx->buffer); /* flush the buffer and states */
+                               JSON_STATE_REMOVE(ctx, JSON_STATE_IN_STRING);
+                               JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY);
+                               break;
+
+                       case '\\' : JSON_STATE_SET(ctx, JSON_STATE_IN_ESCAPE); break;
+                       default  : OSRF_BUFFER_ADD_CHAR( ctx->buffer, c );
+               }
+       }
+       return 0;
+}
+
+
+int _jsonParserHandleNumber( jsonParserContext* ctx ) {
+       char c = ctx->chunk[ctx->index];
+
+       do {
+               OSRF_BUFFER_ADD_CHAR(ctx->buffer, c);
+               c = ctx->chunk[++(ctx->index)];
+       } while( strchr(JSON_NUMBER_CHARS, c) && ctx->index < ctx->chunksize );
+
+       /* if we're run off the end of the chunk and we're not parsing the last chunk,
+        * save the number and the state */
+       if( ctx->index >= ctx->chunksize && 
+                       ! JSON_PARSE_FLAG_CHECK(ctx, JSON_PARSE_LAST_CHUNK) ) {
+               JSON_STATE_SET(ctx, JSON_STATE_IN_NUMBER);
+               return 1;
+       }
+
+       /* make me more strict */
+       char* err = NULL;
+       double d = strtod(ctx->buffer->buf, &err);
+       if(err && err[0] != '\0') 
+               return _jsonParserError(ctx, "Invalid number sequence");
+       JSON_STATE_REMOVE(ctx, JSON_STATE_IN_NUMBER);
+       OSRF_BUFFER_RESET(ctx->buffer);
+       if(ctx->handler->handleNumber)
+               ctx->handler->handleNumber( ctx->userData, d );
+       ctx->index--; /* scooch back to the first non-digit number */
+       return 0;
+}
+
+
+
+
+int jsonParseChunk( jsonParserContext* ctx, char* data, int datalen, int flags ) {
+
+       if( !( ctx && ctx->handler && data && datalen > 0 )) return -1;
+       ctx->chunksize  = datalen;
+       ctx->chunk              = data;
+       ctx->flags              = flags;
+       char c;
+
+       if( JSON_STATE_CHECK(ctx, JSON_STATE_IS_INVALID) )
+               return _jsonParserError( ctx, "JSON Parser cannot continue after an error" );
+
+       if( JSON_STATE_CHECK(ctx, JSON_STATE_IS_DONE) )
+               return _jsonParserError( ctx, "Extra content at end of JSON data" );
+
+       for( ctx->index = 0; (ctx->index < ctx->chunksize) && 
+                               (c = ctx->chunk[ctx->index]); ctx->index++ ) {
+
+               /* middle of parsing a string */
+               if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_STRING)) {
+                       if( _jsonParserHandleString(ctx) == -1 )
+                               return -1;
+                       continue;
+               }
+
+               /* middle of parsing a number */
+               if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_NUMBER) ) {
+                       if( _jsonParserHandleNumber(ctx) == -1 )
+                               return -1;
+                       continue;
+               }
+
+
+#ifdef OSRF_JSON_ALLOW_COMMENTS
+               /* we just saw a bare '/' character */
+               if( JSON_STATE_CHECK(ctx, JSON_STATE_START_COMMENT) ) {
+                       if(c == '*') {
+                               JSON_STATE_REMOVE(ctx, JSON_STATE_START_COMMENT);
+                               JSON_STATE_SET(ctx, JSON_STATE_IN_COMMENT);
+                               continue;
+                       } else {
+                               return _jsonParserError( ctx, "Invalid comment initializer" );
+                       }
+               }
+
+               /* we're currently in the middle of a comment block */
+               if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_COMMENT) ) {
+                       if(c == '*') {
+                               JSON_STATE_REMOVE(ctx, JSON_STATE_IN_COMMENT);
+                               JSON_STATE_SET(ctx, JSON_STATE_END_COMMENT);
+                               continue;
+                       } else {
+                               continue;
+                       }
+               }
+
+               /* we're in a comment, and we just saw a '*' character */
+               if( JSON_STATE_CHECK(ctx, JSON_STATE_END_COMMENT) ) {
+                       if( c == '/' ) { /* comment is finished */
+                               JSON_STATE_REMOVE(ctx, JSON_STATE_END_COMMENT);
+                               continue;
+                       } else {
+                               /* looks like this isn't the end of the comment after all */
+                               JSON_STATE_SET(ctx, JSON_STATE_IN_COMMENT);
+                               JSON_STATE_REMOVE(ctx, JSON_STATE_END_COMMENT);
+                       }
+               }
+#endif
+
+               /* if we're in the middle of parsing a null/true/false sequence */
+               if( JSON_STATE_CHECK(ctx, (JSON_STATE_IN_NULL | 
+                                       JSON_STATE_IN_TRUE | JSON_STATE_IN_FALSE)) ) {
+
+                       int type = (JSON_STATE_CHECK(ctx, JSON_STATE_IN_NULL)) ? 0 :
+                               (JSON_STATE_CHECK(ctx, JSON_STATE_IN_TRUE)) ? 1 : 2;
+
+                       if( _jsonParserHandleMatch( ctx, type ) == -1 ) 
+                               return -1;
+                       continue;
+               }
+
+               JSON_EAT_WS(ctx);
+
+               /* handle all of the top level characters */
+               switch(c) {
+
+                       case '{' : /* starting an object */
+                               if( ctx->handler->handleStartObject) 
+                                       ctx->handler->handleStartObject( ctx->userData );
+                               JSON_STATE_PUSH(ctx, JSON_STATE_IN_OBJECT);
+                               JSON_STATE_SET(ctx, JSON_STATE_IN_KEY);
+                               break;
+
+                       case '}' : /* ending an object */
+                               if( ctx->handler->handleEndObject) 
+                                       ctx->handler->handleEndObject( ctx->userData ); 
+                               JSON_STATE_POP(ctx);
+                               if( JSON_STATE_PEEK(ctx) == NULL )
+                                       JSON_STATE_SET(ctx, JSON_STATE_IS_DONE);
+                               break;
+
+                       case '[' : /* starting an array */
+                               if( ctx->handler->handleStartArray )
+                                       ctx->handler->handleStartArray( ctx->userData );
+                               JSON_STATE_PUSH(ctx, JSON_STATE_IN_ARRAY);
+                               break;
+
+                       case ']': /* ending an array */
+                               if( ctx->handler->handleEndArray )
+                                       ctx->handler->handleEndArray( ctx->userData );
+                               JSON_STATE_POP(ctx);
+                               if( JSON_STATE_PEEK(ctx) == NULL )
+                                       JSON_STATE_SET(ctx, JSON_STATE_IS_DONE);
+                               break;
+                               
+                       case ':' : /* done with the object key */
+                               JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY);
+                               break;
+
+                       case ',' : /* after object or array item */
+                               if( JSON_STATE_CHECK_STACK(ctx, JSON_STATE_IN_OBJECT) )
+                                       JSON_STATE_SET(ctx, JSON_STATE_IN_KEY);
+                               break;
+
+                       case 'n' :
+                       case 'N' : /* null */
+                               if( _jsonParserHandleMatch( ctx, 0 ) == -1)
+                                       return -1;
+                               break;
+
+                       case 't' :
+                       case 'T' :
+                               if( _jsonParserHandleMatch( ctx, 1 ) == -1 )
+                                       return -1;
+                               break;
+
+                       case 'f' :
+                       case 'F' :
+                               if( _jsonParserHandleMatch( ctx, 2 ) == -1)
+                                       return -1;
+                               break;
+
+                       case '"' : 
+                               JSON_STATE_SET(ctx, JSON_STATE_IN_STRING);
+                               break;
+
+#ifdef OSRF_JSON_ALLOW_COMMENTS
+                       case '/' :
+                               JSON_STATE_SET(ctx, JSON_STATE_START_COMMENT);
+                               break;
+#endif
+
+                       default:
+                               if( strchr(JSON_NUMBER_CHARS, c) ) {
+                                       if( _jsonParserHandleNumber( ctx ) == -1 )
+                                               return -1;
+                               } else {
+                                       return _jsonParserError( ctx, "Invalid Token" );
+                               }
+               }
+       }
+
+       return 0;
+}
+
+
+jsonInternalParser* _jsonNewInternalParser() {
+       jsonInternalParser* p;
+       OSRF_MALLOC(p, sizeof(jsonInternalParser));
+       p->ctx = jsonNewParser( jsonInternalParserHandler, p );
+       p->obj          = NULL;
+       p->lastkey      = NULL;
+       return p;
+}
+
+void _jsonInternalParserFree(jsonInternalParser* p) {
+       if(!p) return;
+       jsonParserFree(p->ctx);
+       free(p->lastkey);
+       free(p);
+}
+
+static jsonObject* _jsonParseStringImpl(char* str, void (*errorHandler) (const char*) ) {
+       jsonInternalParser* parser = _jsonNewInternalParser();
+       parser->handleError = errorHandler;
+       jsonParseChunk( parser->ctx, str, strlen(str),  JSON_PARSE_LAST_CHUNK );
+       jsonObject* obj = parser->obj;
+       _jsonInternalParserFree(parser);
+       return obj;
+}
+
+jsonObject* jsonParseStringHandleError( 
+               void (*errorHandler) (const char*), char* str, ... ) {
+       if(!str) return NULL;
+       VA_LIST_TO_STRING(str);
+       return _jsonParseStringImpl(VA_BUF, errorHandler);
+}
+
+jsonObject* jsonParseString( char* str ) {
+       if(!str) return NULL;
+       jsonObject* obj =  _jsonParseStringImpl(str, NULL);
+       jsonObject* obj2 = jsonObjectDecodeClass(obj);
+       jsonObjectFree(obj);
+       return obj2;
+}
+
+jsonObject* jsonParseStringRaw( char* str ) {
+       if(!str) return NULL;
+       return _jsonParseStringImpl(str, NULL);
+}
+
+jsonObject* jsonParseStringFmt( char* str, ... ) {
+       if(!str) return NULL;
+       VA_LIST_TO_STRING(str);
+       return _jsonParseStringImpl(VA_BUF, NULL);
+}
+
+
+#define JSON_SHOVE_ITEM(ctx,type)  \
+       jsonInternalParser* p = (jsonInternalParser*) ctx;\
+       _jsonInsertParserItem(p, jsonNewObjectType(type));
+
+void _jsonHandleStartObject(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_HASH); }
+void _jsonHandleStartArray(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_ARRAY); }
+void _jsonHandleNull(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_NULL); }
+
+void _jsonHandleObjectKey(void* ctx, char* key) {
+       jsonInternalParser* p = (jsonInternalParser*) ctx;
+       free(p->lastkey);
+       p->lastkey = strdup(key);
+}
+
+void _jsonHandleEndObject(void* ctx) {
+       jsonInternalParser* p = (jsonInternalParser*) ctx;
+       p->current = p->current->parent;
+}
+
+void _jsonHandleEndArray(void* ctx) {
+       jsonInternalParser* p = (jsonInternalParser*) ctx;
+       p->current = p->current->parent;
+}
+
+void _jsonHandleString(void* ctx, char* string) {
+       jsonInternalParser* p = (jsonInternalParser*) ctx;
+       _jsonInsertParserItem(p, jsonNewObject(string));
+}
+
+void _jsonHandleBool(void* ctx, int boolval) {
+       jsonInternalParser* p = (jsonInternalParser*) ctx;
+       jsonObject* obj = jsonNewObjectType(JSON_BOOL);
+       obj->value.b = boolval;
+       _jsonInsertParserItem(p, obj);
+}
+
+void _jsonHandleNumber(void* ctx, double num) {
+       jsonInternalParser* p = (jsonInternalParser*) ctx;
+       _jsonInsertParserItem(p, jsonNewNumberObject(num));
+}
+
+void _jsonHandleError(void* ctx, char* str, ...) {
+       jsonInternalParser* p = (jsonInternalParser*) ctx;
+       VA_LIST_TO_STRING(str);
+
+       if( p->handleError ) 
+               p->handleError(VA_BUF);
+       else 
+               if( jsonClientErrorCallback ) 
+                       jsonClientErrorCallback(VA_BUF);
+
+       else fprintf(stderr, "%s\n", VA_BUF);
+       jsonObjectFree(p->obj);
+       p->obj = NULL;
+}
+
+
+void _jsonInsertParserItem( jsonInternalParser* p, jsonObject* newo ) {
+
+       if( !p->obj ) {
+
+               /* new parser, set the new object to our object */
+               p->obj = p->current = newo;
+
+       } else {
+
+               /* insert the new object into the current container object */
+               switch(p->current->type) { 
+                       case JSON_HASH  : jsonObjectSetKey(p->current, p->lastkey, newo);  break;
+                       case JSON_ARRAY: jsonObjectPush(p->current, newo); break;
+                       default: fprintf(stderr, "%s:%d -> how?\n", JSON_LOG_MARK); 
+               } 
+
+               /* if the new object is a container object, make it our current container */
+               if( newo->type == JSON_ARRAY || newo->type == JSON_HASH )
+                       p->current = newo;      
+       }
+}
+
+
diff --git a/src/libopensrf/osrf_json_test.c b/src/libopensrf/osrf_json_test.c
new file mode 100644 (file)
index 0000000..b3df610
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Basic JSON test module.  Needs more strenous tests....
+ *
+ */
+#include <stdio.h>
+#include <opensrf/osrf_json.h>
+
+static void speedTest();
+
+
+int main(int argc, char* argv[]) {
+    /* XXX add support for command line test type specification */
+    speedTest(); 
+    return 0; 
+}
+
+
+
+static void speedTest() {
+
+    /* creates a giant json object, generating JSON strings
+     * of subobjects as it goes. */
+
+    int i,k;
+    int count = 50;
+    char buf[16];
+    char* jsonString;
+
+    jsonObject* array;
+    jsonObject* dupe;
+    jsonObject* hash = jsonNewObject(NULL);
+
+    for(i = 0; i < count; i++) {
+        
+        memset(buf, 0, 16);
+        snprintf(buf, 16, "key_%d", i);
+
+        array = jsonNewObject(NULL);
+        for(k = 0; k < count + i; k++) {
+            jsonObjectPush(array, jsonNewNumberObject(k));
+            jsonObjectPush(array, jsonNewObject(NULL));
+            jsonObjectPush(array, jsonNewObjectFmt("str %d-%d", i, k));
+        }
+        jsonObjectSetKey(hash, buf, array);
+
+        jsonString = jsonObjectToJSON(hash);
+        printf("%s\n\n", jsonString);
+        dupe = jsonParseString(jsonString);
+
+        jsonObjectFree(dupe);
+        free(jsonString);
+    }
+
+    jsonObjectFree(hash);
+}
+
diff --git a/src/libopensrf/osrf_json_tools.c b/src/libopensrf/osrf_json_tools.c
new file mode 100644 (file)
index 0000000..5476bba
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+Copyright (C) 2006  Georgia Public Library Service 
+Bill Erickson <billserickson@gmail.com>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+*/
+
+#include <opensrf/osrf_json.h>
+
+jsonObject* _jsonObjectEncodeClass( jsonObject* obj, int ignoreClass );
+
+
+jsonObject* jsonObjectFindPath( const jsonObject* obj, char* path, ...);
+jsonObject* _jsonObjectFindPathRecurse(const jsonObject* obj, char* root, char* path);
+jsonObject* __jsonObjectFindPathRecurse(const jsonObject* obj, char* root);
+
+
+static char* __tabs(int count) {
+       growing_buffer* buf = buffer_init(24);
+       int i;
+       for(i=0;i<count;i++) OSRF_BUFFER_ADD(buf, "  ");
+    return buffer_release(buf);
+}
+
+char* jsonFormatString( const char* string ) {
+       if(!string) return strdup("");
+
+       growing_buffer* buf = buffer_init(64);
+       int i;
+       int depth = 0;
+       char* tab = NULL;
+
+       char c;
+       for(i=0; i!= strlen(string); i++) {
+               c = string[i];
+
+               if( c == '{' || c == '[' ) {
+
+                       tab = __tabs(++depth);
+                       buffer_fadd( buf, "%c\n%s", c, tab);
+                       free(tab);
+
+               } else if( c == '}' || c == ']' ) {
+
+                       tab = __tabs(--depth);
+                       buffer_fadd( buf, "\n%s%c", tab, c);
+                       free(tab);
+
+                       if(string[i+1] != ',') {
+                               tab = __tabs(depth);
+                               buffer_fadd( buf, "%s", tab );  
+                               free(tab);
+                       }
+
+               } else if( c == ',' ) {
+
+                       tab = __tabs(depth);
+                       buffer_fadd(buf, ",\n%s", tab);
+                       free(tab);
+
+               } else { buffer_add_char(buf, c); }
+
+       }
+
+    return buffer_release(buf);
+}
+
+
+
+jsonObject* jsonObjectDecodeClass( jsonObject* obj ) {
+       if(!obj) return jsonNewObject(NULL);
+
+       jsonObject* newObj              = NULL; 
+       jsonObject* classObj            = NULL; 
+       jsonObject* payloadObj  = NULL;
+       int i;
+
+       if( obj->type == JSON_HASH ) {
+
+               /* are we a special class object? */
+               if( (classObj = jsonObjectGetKey( obj, JSON_CLASS_KEY )) ) {
+
+                       /* do we have a payload */
+                       if( (payloadObj = jsonObjectGetKey( obj, JSON_DATA_KEY )) ) {
+                               newObj = jsonObjectDecodeClass( payloadObj ); 
+                               jsonObjectSetClass( newObj, jsonObjectGetString(classObj) );
+
+                       } else { /* class is defined but there is no payload */
+                               return NULL;
+                       }
+
+               } else { /* we're a regular hash */
+
+                       jsonIterator* itr = jsonNewIterator(obj);
+                       jsonObject* tmp;
+                       newObj = jsonNewObjectType(JSON_HASH);
+                       while( (tmp = jsonIteratorNext(itr)) ) {
+                               jsonObject* o = jsonObjectDecodeClass(tmp);
+                               jsonObjectSetKey( newObj, itr->key, o );
+                       }
+                       jsonIteratorFree(itr);
+               }
+
+       } else {
+
+               if( obj->type == JSON_ARRAY ) { /* we're an array */
+                       newObj = jsonNewObjectType(JSON_ARRAY);
+                       for( i = 0; i != obj->size; i++ ) {
+                               jsonObject* tmp = jsonObjectDecodeClass(jsonObjectGetIndex( obj, i ) );
+                               jsonObjectSetIndex( newObj, i, tmp );
+                       }
+
+               } else { /* not an aggregate type */
+                       newObj = jsonObjectClone(obj);
+               }
+       }
+               
+       return newObj;
+}
+
+jsonObject* jsonObjectEncodeClass( jsonObject* obj ) {
+       return _jsonObjectEncodeClass( obj, 0 );
+}
+
+jsonObject* _jsonObjectEncodeClass( jsonObject* obj, int ignoreClass ) {
+
+       //if(!obj) return NULL;
+       if(!obj) return jsonNewObject(NULL);
+       jsonObject* newObj = NULL;
+
+       if( obj->classname && ! ignoreClass ) {
+               newObj = jsonNewObjectType(JSON_HASH);
+
+               jsonObjectSetKey( newObj, 
+                       JSON_CLASS_KEY, jsonNewObject(obj->classname) ); 
+
+               jsonObjectSetKey( newObj, 
+                       JSON_DATA_KEY, _jsonObjectEncodeClass(obj, 1));
+
+       } else if( obj->type == JSON_HASH ) {
+
+               jsonIterator* itr = jsonNewIterator(obj);
+               jsonObject* tmp;
+               newObj = jsonNewObjectType(JSON_HASH);
+
+               while( (tmp = jsonIteratorNext(itr)) ) {
+                       jsonObjectSetKey( newObj, itr->key, 
+                                       _jsonObjectEncodeClass(tmp, 0));
+               }
+               jsonIteratorFree(itr);
+
+       } else if( obj->type == JSON_ARRAY ) {
+
+               newObj = jsonNewObjectType(JSON_ARRAY);
+               int i;
+               for( i = 0; i != obj->size; i++ ) {
+                       jsonObjectSetIndex( newObj, i, 
+                               _jsonObjectEncodeClass(jsonObjectGetIndex( obj, i ), 0 ));
+               }
+
+       } else {
+               newObj = jsonObjectClone(obj);
+       }
+
+       return newObj;
+}
+
+jsonObject* jsonParseFile( char* filename ) {
+       if(!filename) return NULL;
+       char* data = file_to_string(filename);
+       jsonObject* o = jsonParseString(data);
+       free(data);
+       return o;
+}
+
+
+
+jsonObject* jsonObjectFindPath( const jsonObject* obj, char* format, ...) {
+       if(!obj || !format || strlen(format) < 1) return NULL;  
+
+       VA_LIST_TO_STRING(format);
+       char* buf = VA_BUF;
+       char* token = NULL;
+       char* t = buf;
+       char* tt; /* strtok storage */
+
+       /* copy the path before strtok_r destroys it */
+       char* pathcopy = strdup(buf);
+
+       /* grab the root of the path */
+       token = strtok_r(t, "/", &tt);
+       if(!token) return NULL;
+
+       /* special case where path starts with //  (start anywhere) */
+       if(strlen(pathcopy) > 2 && pathcopy[0] == '/' && pathcopy[1] == '/') {
+               jsonObject* it = _jsonObjectFindPathRecurse(obj, token, pathcopy + 1);
+               free(pathcopy);
+               return it;
+       }
+
+       free(pathcopy);
+
+       t = NULL;
+       do { 
+               obj = jsonObjectGetKey(obj, token);
+       } while( (token = strtok_r(NULL, "/", &tt)) && obj);
+
+       return jsonObjectClone(obj);
+}
+
+/* --------------------------------------------------------------- */
+
+
+
+jsonObject* _jsonObjectFindPathRecurse(const jsonObject* obj, char* root, char* path) {
+
+       if(!obj || ! root || !path) return NULL;
+
+       /* collect all of the potential objects */
+       jsonObject* arr = __jsonObjectFindPathRecurse(obj, root);
+
+       /* container for fully matching objects */
+       jsonObject* newarr = jsonParseString("[]");
+       int i;
+
+       /* path is just /root or /root/ */
+       if( strlen(root) + 2 >= strlen(path) ) {
+               return arr;
+
+       } else {
+
+               /* gather all of the sub-objects that match the full path */
+               for( i = 0; i < arr->size; i++ ) {
+                       jsonObject* a = jsonObjectGetIndex(arr, i);
+                       jsonObject* thing = jsonObjectFindPath(a , path + strlen(root) + 1); 
+
+                       if(thing) { //jsonObjectPush(newarr, thing);
+               if(thing->type == JSON_ARRAY) {
+               int i;
+                                       for( i = 0; i != thing->size; i++ )
+                                               jsonObjectPush(newarr, jsonObjectClone(jsonObjectGetIndex(thing,i)));
+                                       jsonObjectFree(thing);
+
+                               } else {
+                                       jsonObjectPush(newarr, thing);
+                               }                                               
+                       }
+               }
+       }
+       
+       jsonObjectFree(arr);
+       return newarr;
+}
+
+jsonObject* __jsonObjectFindPathRecurse(const jsonObject* obj, char* root) {
+
+       jsonObject* arr = jsonParseString("[]");
+       if(!obj) return arr;
+
+       int i;
+
+       /* if the current object has a node that matches, add it */
+
+       jsonObject* o = jsonObjectGetKey(obj, root);
+       if(o) jsonObjectPush( arr, jsonObjectClone(o) );
+
+       jsonObject* tmp = NULL;
+       jsonObject* childarr;
+       jsonIterator* itr = jsonNewIterator(obj);
+
+       /* recurse through the children and find all potential nodes */
+       while( (tmp = jsonIteratorNext(itr)) ) {
+               childarr = __jsonObjectFindPathRecurse(tmp, root);
+               if(childarr && childarr->size > 0) {
+                       for( i = 0; i!= childarr->size; i++ ) {
+                               jsonObjectPush( arr, jsonObjectClone(jsonObjectGetIndex(childarr, i)) );
+                       }
+               }
+               jsonObjectFree(childarr);
+       }
+
+       jsonIteratorFree(itr);
+
+       return arr;
+}
+
+
+
+
diff --git a/src/libopensrf/osrf_json_xml.c b/src/libopensrf/osrf_json_xml.c
new file mode 100644 (file)
index 0000000..283daf4
--- /dev/null
@@ -0,0 +1,365 @@
+#include <opensrf/osrf_json_xml.h>
+
+#ifdef OSRF_JSON_ENABLE_XML_UTILS
+
+struct osrfXMLGatewayParserStruct {
+    osrfList* objStack;
+    osrfList* keyStack;
+    jsonObject* obj;
+    short inString;
+    short inNumber;
+    short error;
+};
+typedef struct osrfXMLGatewayParserStruct osrfXMLGatewayParser;
+
+/** returns the attribute value with the given attribute name */
+static char* getXMLAttr(const xmlChar** atts, char* attr_name) {
+    int i;
+    if (atts != NULL) {
+        for(i = 0; (atts[i] != NULL); i++) {
+            if(strcmp((char*) atts[i++], attr_name) == 0) {
+                if(atts[i] != NULL) 
+                    return (char*) atts[i];
+            }
+        }
+    }
+    return NULL;
+}
+
+
+static void appendChild(osrfXMLGatewayParser* p, jsonObject* obj) {
+
+    if(p->obj == NULL) 
+        p->obj = obj;
+
+    if(p->objStack->size == 0)
+        return;
+    
+    jsonObject* parent = OSRF_LIST_GET_INDEX(p->objStack, p->objStack->size - 1);
+
+    if(parent->type == JSON_ARRAY) {
+        jsonObjectPush(parent, obj);
+    } else {
+        char* key = osrfListPop(p->keyStack);
+        jsonObjectSetKey(parent, key, obj);
+        free(key); /* the list is not setup for auto-freeing */
+    }
+}
+
+
+
+static void startElementHandler(
+    void *parser, const xmlChar *name, const xmlChar **atts) {
+
+    osrfXMLGatewayParser* p = (osrfXMLGatewayParser*) parser;
+    jsonObject* obj;
+
+    char* hint = getXMLAttr(atts, "class_hint");
+
+    if(!strcmp((char*) name, "null")) {
+        appendChild(p, jsonNewObject(NULL));
+        return;
+    }
+
+    if(!strcmp((char*) name, "string")) {
+        p->inString = 1;
+        return;
+    }
+
+    if(!strcmp((char*) name, "element")) {
+       osrfListPush(p->keyStack, strdup(getXMLAttr(atts, "key")));
+       return;
+    }
+
+    if(!strcmp((char*) name, "object")) {
+        obj = jsonNewObject(NULL);
+        jsonObjectSetClass(obj, hint); /* OK if hint is NULL */
+        obj->type = JSON_HASH;
+        appendChild(p, obj);
+        osrfListPush(p->objStack, obj);
+        return;
+    }
+
+    if(!strcmp((char*) name, "array")) {
+        obj = jsonNewObject(NULL);
+        jsonObjectSetClass(obj, hint); /* OK if hint is NULL */
+        obj->type = JSON_ARRAY;
+        appendChild(p, obj);
+        osrfListPush(p->objStack, obj);
+        return;
+    }
+
+
+    if(!strcmp((char*) name, "number")) {
+        p->inNumber = 1;
+        return;
+    }
+
+    if(!strcmp((char*) name, "boolean")) {
+        obj = jsonNewObject(NULL);
+        obj->type = JSON_BOOL;
+        char* val = getXMLAttr(atts, "value");
+        if(val && !strcmp(val, "true"))
+            obj->value.b = 1;
+        
+        return;
+    }
+}
+
+static void endElementHandler( void *parser, const xmlChar *name) {
+    if(!strcmp((char*) name, "array") || !strcmp((char*) name, "object")) {
+        osrfXMLGatewayParser* p = (osrfXMLGatewayParser*) parser;
+        osrfListPop(p->objStack);
+    }
+}
+
+static void characterHandler(void *parser, const xmlChar *ch, int len) {
+
+    char data[len+1];
+    strncpy(data, (char*) ch, len);
+    data[len] = '\0';
+    osrfXMLGatewayParser* p = (osrfXMLGatewayParser*) parser;
+
+    if(p->inString) {
+        appendChild(p, jsonNewObject(data));
+        p->inString = 0;
+        return;
+    }
+
+    if(p->inNumber) {
+        appendChild(p, jsonNewNumberObject(atof(data)));
+        p->inNumber = 0;
+        return;
+    }
+}
+
+static void parseWarningHandler(void *parser, const char* msg, ...) {
+    VA_LIST_TO_STRING(msg);
+    fprintf(stderr, "Parser warning %s\n", VA_BUF);
+    fflush(stderr);
+}
+
+static void parseErrorHandler(void *parser, const char* msg, ...) {
+
+    VA_LIST_TO_STRING(msg);
+    fprintf(stderr, "Parser error %s\n", VA_BUF);
+    fflush(stderr);
+
+    osrfXMLGatewayParser* p = (osrfXMLGatewayParser*) parser;
+
+    /*  keyStack as strdup'ed strings.  The list may
+     *  not be empty, so tell it to free the items
+     *  when it's freed (from the main routine)
+     */
+    osrfListSetDefaultFree(p->keyStack);
+    jsonObjectFree(p->obj);
+
+    p->obj = NULL;
+    p->error = 1;
+}
+
+
+
+
+static xmlSAXHandler SAXHandlerStruct = {
+    NULL,                            /* internalSubset */
+    NULL,                            /* isStandalone */
+    NULL,                            /* hasInternalSubset */
+    NULL,                            /* hasExternalSubset */
+    NULL,                            /* resolveEntity */
+    NULL,                            /* getEntity */
+    NULL,                            /* entityDecl */
+    NULL,                            /* notationDecl */
+    NULL,                            /* attributeDecl */
+    NULL,                            /* elementDecl */
+    NULL,                            /* unparsedEntityDecl */
+    NULL,                            /* setDocumentLocator */
+    NULL,                            /* startDocument */
+    NULL,                            /* endDocument */
+    startElementHandler,        /* startElement */
+    endElementHandler,      /* endElement */
+    NULL,                            /* reference */
+    characterHandler,           /* characters */
+    NULL,                            /* ignorableWhitespace */
+    NULL,                            /* processingInstruction */
+    NULL,                            /* comment */
+    parseWarningHandler,     /* xmlParserWarning */
+    parseErrorHandler,       /* xmlParserError */
+    NULL,                            /* xmlParserFatalError : unused */
+    NULL,                            /* getParameterEntity */
+    NULL,                            /* cdataBlock; */
+    NULL,                            /* externalSubset; */
+    1,
+    NULL,
+    NULL,                            /* startElementNs */
+    NULL,                            /* endElementNs */
+    NULL                            /* xmlStructuredErrorFunc */
+};
+
+static const xmlSAXHandlerPtr SAXHandler = &SAXHandlerStruct;
+
+jsonObject* jsonXMLToJSONObject(const char* xml) {
+
+    osrfXMLGatewayParser parser;
+
+    /* don't define freeItem, since objects will be cleaned by freeing the parent */
+    parser.objStack = osrfNewList(); 
+    /* don't define freeItem, since the list eill end up empty if there are no errors*/
+    parser.keyStack = osrfNewList(); 
+    parser.obj = NULL;
+    parser.inString = 0;
+    parser.inNumber = 0;
+
+    xmlParserCtxtPtr ctxt = xmlCreatePushParserCtxt(SAXHandler, &parser, "", 0, NULL);
+    xmlParseChunk(ctxt, xml, strlen(xml), 1);
+
+    osrfListFree(parser.objStack);
+    osrfListFree(parser.keyStack);
+    xmlFreeParserCtxt(ctxt);
+    xmlCleanupCharEncodingHandlers();
+    xmlDictCleanup();
+    xmlCleanupParser();
+
+    return parser.obj;
+}
+
+
+
+
+
+
+static char* _escape_xml (char*);
+static int _recurse_jsonObjectToXML(jsonObject*, growing_buffer*);
+
+char* jsonObjectToXML(jsonObject* obj) {
+
+       growing_buffer * res_xml;
+       char * output;
+
+       res_xml = buffer_init(1024);
+
+       if (!obj)
+               return strdup("<null/>");
+       
+       _recurse_jsonObjectToXML( obj, res_xml );
+       output = buffer_data(res_xml);
+       
+       buffer_free(res_xml);
+
+       return output;
+
+}
+
+int _recurse_jsonObjectToXML(jsonObject* obj, growing_buffer* res_xml) {
+
+       char * hint = NULL;
+       char * bool_val = NULL;
+       int i = 0;
+       
+       if (obj->classname)
+               hint = strdup(obj->classname);
+
+       if(obj->type == JSON_NULL) {
+
+               if (hint)
+                       buffer_fadd(res_xml, "<null class_hint=\"%s\"/>",hint);
+               else
+                       buffer_add(res_xml, "<null/>");
+
+       } else if(obj->type == JSON_BOOL) {
+
+               if (obj->value.b)
+                       bool_val = strdup("true");
+               else
+                       bool_val = strdup("false");
+
+               if (hint)
+                       buffer_fadd(res_xml, "<boolean value=\"%s\" class_hint=\"%s\"/>", bool_val, hint);
+               else
+                       buffer_fadd(res_xml, "<boolean value=\"%s\"/>", bool_val);
+
+               free(bool_val);
+                
+       } else if (obj->type == JSON_STRING) {
+               if (hint) {
+                       char * t = _escape_xml(jsonObjectGetString(obj));
+                       buffer_fadd(res_xml,"<string class_hint=\"%s\">%s</string>", hint, t);
+                       free(t);
+               } else {
+                       char * t = _escape_xml(jsonObjectGetString(obj));
+                       buffer_fadd(res_xml,"<string>%s</string>", t);
+                       free(t);
+               }
+
+       } else if(obj->type == JSON_NUMBER) {
+               double x = jsonObjectGetNumber(obj);
+               if (hint) {
+                       if (x == (int)x)
+                               buffer_fadd(res_xml,"<number class_hint=\"%s\">%d</number>", hint, (int)x);
+                       else
+                               buffer_fadd(res_xml,"<number class_hint=\"%s\">%lf</number>", hint, x);
+               } else {
+                       if (x == (int)x)
+                               buffer_fadd(res_xml,"<number>%d</number>", (int)x);
+                       else
+                               buffer_fadd(res_xml,"<number>%lf</number>", x);
+               }
+
+       } else if (obj->type == JSON_ARRAY) {
+
+               if (hint) 
+                       buffer_fadd(res_xml,"<array class_hint=\"%s\">", hint);
+               else
+                               buffer_add(res_xml,"<array>");
+
+               for ( i = 0; i!= obj->size; i++ )
+                       _recurse_jsonObjectToXML(jsonObjectGetIndex(obj,i), res_xml);
+
+               buffer_add(res_xml,"</array>");
+
+       } else if (obj->type == JSON_HASH) {
+
+               if (hint)
+                       buffer_fadd(res_xml,"<object class_hint=\"%s\">", hint);
+               else
+                       buffer_add(res_xml,"<object>");
+
+               jsonIterator* itr = jsonNewIterator(obj);
+               jsonObject* tmp;
+               while( (tmp = jsonIteratorNext(itr)) ) {
+                       buffer_fadd(res_xml,"<element key=\"%s\">",itr->key);
+                       _recurse_jsonObjectToXML(tmp, res_xml);
+                       buffer_add(res_xml,"</element>");
+               }
+               jsonIteratorFree(itr);
+
+               buffer_add(res_xml,"</object>");
+       }
+
+       if (hint)
+               free(hint);
+
+       return 1;
+}
+
+char* _escape_xml (char* text) {
+       char* out;
+       growing_buffer* b = buffer_init(256);
+       int len = strlen(text);
+       int i;
+       for (i = 0; i < len; i++) {
+               if (text[i] == '&')
+                       buffer_add(b,"&amp;");
+               else if (text[i] == '<')
+                       buffer_add(b,"&lt;");
+               else if (text[i] == '>')
+                       buffer_add(b,"&gt;");
+               else
+                       buffer_add_char(b,text[i]);
+       }
+       out = buffer_data(b);
+       buffer_free(b);
+       return out;
+}
+
+#endif
diff --git a/src/libopensrf/osrf_legacy_json.c b/src/libopensrf/osrf_legacy_json.c
new file mode 100644 (file)
index 0000000..69b9867
--- /dev/null
@@ -0,0 +1,880 @@
+/*
+Copyright (C) 2006  Georgia Public Library Service 
+Bill Erickson <billserickson@gmail.com>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+*/
+
+
+#include <opensrf/osrf_legacy_json.h>
+
+/* keep a copy of the length of the current json string so we don't 
+ * have to calculate it in each function
+ */
+int current_strlen; 
+
+
+jsonObject* legacy_jsonParseString( char* string) {
+       return json_parse_string( string );
+}
+
+jsonObject* legacy_jsonParseStringFmt( char* string, ... ) {
+       VA_LIST_TO_STRING(string);
+       return json_parse_string( VA_BUF );
+}
+
+
+jsonObject* json_parse_string(char* string) {
+
+       if(string == NULL) return NULL;
+
+       current_strlen = strlen(string);
+
+       if(current_strlen == 0) 
+               return NULL;
+
+       unsigned long index = 0;
+
+       json_eat_ws(string, &index, 1, current_strlen); /* remove leading whitespace */
+       if(index == current_strlen) return NULL;
+
+       jsonObject* obj = jsonNewObject(NULL);
+
+       int status = _json_parse_string(string, &index, obj, current_strlen);
+       if(!status) return obj;
+
+       if(status == -2) {
+               jsonObjectFree(obj);
+               return NULL;
+       }
+
+       return NULL;
+}
+
+
+int _json_parse_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
+       if( !string || !index || *index >= current_strlen) return -2;
+
+       int status = 0; /* return code from parsing routines */
+       char* classname = NULL; /* object class hint */
+       json_eat_ws(string, index, 1, current_strlen); /* remove leading whitespace */
+
+       char c = string[*index];
+
+       /* remove any leading comments */
+       if( c == '/' ) { 
+
+               while(1) {
+                       (*index)++; /* move to second comment char */
+                       status = json_eat_comment(string, index, &classname, 1, current_strlen);
+                       if(status) return status;
+
+                       json_eat_ws(string, index, 1, current_strlen);
+                       c = string[*index];
+                       if(c != '/')
+                               break;
+               }
+       }
+
+       json_eat_ws(string, index, 1, current_strlen); /* remove leading whitespace */
+
+       if(*index >= current_strlen)
+               return -2;
+
+       switch(c) {
+                               
+               /* json string */
+               case '"': 
+                       (*index)++;
+                       status = json_parse_json_string(string, index, obj, current_strlen); break;
+
+               /* json array */
+               case '[':
+                       (*index)++;
+                       status = json_parse_json_array(string, index, obj, current_strlen);                     
+                       break;
+
+               /* json object */
+               case '{':
+                       (*index)++;
+                       status = json_parse_json_object(string, index, obj, current_strlen);
+                       break;
+
+               /* NULL */
+               case 'n':
+               case 'N':
+                       status = json_parse_json_null(string, index, obj, current_strlen);
+                       break;
+                       
+
+               /* true, false */
+               case 'f':
+               case 'F':
+               case 't':
+               case 'T':
+                       status = json_parse_json_bool(string, index, obj, current_strlen);
+                       break;
+
+               default:
+                       if(isdigit(c) || c == '.' || c == '-') { /* are we a number? */
+                               status = json_parse_json_number(string, index, obj, current_strlen);    
+                               if(status) return status;
+                               break;
+                       }
+
+                       (*index)--;
+                       /* we should never get here */
+                       return json_handle_error(string, index, "_json_parse_string() final switch clause");
+       }       
+
+       if(status) return status;
+
+       json_eat_ws(string, index, 1, current_strlen);
+
+       if( *index < current_strlen ) {
+               /* remove any trailing comments */
+               c = string[*index];
+               if( c == '/' ) { 
+                       (*index)++;
+                       status = json_eat_comment(string, index, NULL, 0, current_strlen);
+                       if(status) return status;
+               }
+       }
+
+       if(classname){
+               jsonObjectSetClass(obj, classname);
+               free(classname);
+       }
+
+       return 0;
+}
+
+
+int json_parse_json_null(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
+
+       if(*index >= (current_strlen - 3)) {
+               return json_handle_error(string, index, 
+                       "_parse_json_null(): invalid null" );
+       }
+
+       if(!strncasecmp(string + (*index), "null", 4)) {
+               (*index) += 4;
+               obj->type = JSON_NULL;
+               return 0;
+       } else {
+               return json_handle_error(string, index,
+                       "_parse_json_null(): invalid null" );
+       }
+}
+
+/* should be at the first character of the bool at this point */
+int json_parse_json_bool(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
+       if( ! string || ! obj || *index >= current_strlen ) return -1;
+
+       char* ret = "json_parse_json_bool(): truncated bool";
+
+       if( *index >= (current_strlen - 5))
+               return json_handle_error(string, index, ret);
+       
+       if(!strncasecmp( string + (*index), "false", 5)) {
+               (*index) += 5;
+               obj->value.b = 0;
+               obj->type = JSON_BOOL;
+               return 0;
+       }
+
+       if( *index >= (current_strlen - 4))
+               return json_handle_error(string, index, ret);
+
+       if(!strncasecmp( string + (*index), "true", 4)) {
+               (*index) += 4;
+               obj->value.b = 1;
+               obj->type = JSON_BOOL;
+               return 0;
+       }
+
+       return json_handle_error(string, index, ret);
+}
+
+
+/* expecting the first character of the number */
+int json_parse_json_number(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
+       if( ! string || ! obj || *index >= current_strlen ) return -1;
+
+       growing_buffer* buf = buffer_init(64);
+       char c = string[*index];
+
+       int done = 0;
+       int dot_seen = 0;
+
+       /* negative number? */
+       if(c == '-') { buffer_add(buf, "-"); (*index)++; }
+
+       c = string[*index];
+
+       while(*index < current_strlen) {
+
+               if(isdigit(c)) {
+                       buffer_add_char(buf, c);
+               }
+
+               else if( c == '.' ) {
+                       if(dot_seen) {
+                               buffer_free(buf);
+                               return json_handle_error(string, index, 
+                                       "json_parse_json_number(): malformed json number");
+                       }
+                       dot_seen = 1;
+                       buffer_add_char(buf, c);
+               } else {
+                       done = 1; break;
+               }
+
+               (*index)++;
+               c = string[*index];
+               if(done) break;
+       }
+
+       obj->type = JSON_NUMBER;
+       obj->value.n = strtod(buf->buf, NULL);
+       buffer_free(buf);
+       return 0;
+}
+
+/* index should point to the character directly following the '['.  when done
+ * index will point to the character directly following the ']' character
+ */
+int json_parse_json_array(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
+
+       if( ! string || ! obj || ! index || *index >= current_strlen ) return -1;
+
+       int status = 0;
+       int in_parse = 0; /* true if this array already contains one item */
+       obj->type = JSON_ARRAY;
+       int set = 0;
+       int done = 0;
+
+       while(*index < current_strlen) {
+
+               json_eat_ws(string, index, 1, current_strlen);
+
+               if(string[*index] == ']') {
+                       (*index)++;
+                       done = 1;
+                       break;
+               }
+
+               if(in_parse) {
+                       json_eat_ws(string, index, 1, current_strlen);
+                       if(string[*index] != ',') {
+                               return json_handle_error(string, index,
+                                       "json_parse_json_array(): array item not followed by a ','");
+                       }
+                       (*index)++;
+                       json_eat_ws(string, index, 1, current_strlen);
+               }
+
+               jsonObject* item = jsonNewObject(NULL);
+
+               #ifndef STRICT_JSON_READ
+               if(*index < current_strlen) {
+                       if(string[*index] == ',' || string[*index] == ']') {
+                               status = 0;
+                               set = 1;
+                       }
+               }
+               if(!set) status = _json_parse_string(string, index, item, current_strlen);
+
+               #else
+               status = _json_parse_string(string, index, item, current_strlen);
+               #endif
+
+               if(status) { jsonObjectFree(item); return status; }
+               jsonObjectPush(obj, item);
+               in_parse = 1;
+               set = 0;
+       }
+
+       if(!done)
+               return json_handle_error(string, index,
+                       "json_parse_json_array(): array not closed");
+
+       return 0;
+}
+
+
+/* index should point to the character directly following the '{'.  when done
+ * index will point to the character directly following the '}'
+ */
+int json_parse_json_object(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
+       if( ! string || !obj || ! index || *index >= current_strlen ) return -1;
+
+       obj->type = JSON_HASH;
+       int status;
+       int in_parse = 0; /* true if we've already added one item to this object */
+       int set = 0;
+       int done = 0;
+
+       while(*index < current_strlen) {
+
+               json_eat_ws(string, index, 1, current_strlen);
+
+               if(string[*index] == '}') {
+                       (*index)++;
+                       done = 1;
+                       break;
+               }
+
+               if(in_parse) {
+                       if(string[*index] != ',') {
+                               return json_handle_error(string, index,
+                                       "json_parse_json_object(): object missing ',' between elements" );
+                       }
+                       (*index)++;
+                       json_eat_ws(string, index, 1, current_strlen);
+               }
+
+               /* first we grab the hash key */
+               jsonObject* key_obj = jsonNewObject(NULL);
+               status = _json_parse_string(string, index, key_obj, current_strlen);
+               if(status) return status;
+
+               if(key_obj->type != JSON_STRING) {
+                       return json_handle_error(string, index, 
+                               "_json_parse_json_object(): hash key not a string");
+               }
+
+               char* key = key_obj->value.s;
+
+               json_eat_ws(string, index, 1, current_strlen);
+
+               if(string[*index] != ':') {
+                       return json_handle_error(string, index, 
+                               "json_parse_json_object(): hash key not followed by ':' character");
+               }
+
+               (*index)++;
+
+               /* now grab the value object */
+               json_eat_ws(string, index, 1, current_strlen);
+               jsonObject* value_obj = jsonNewObject(NULL);
+
+#ifndef STRICT_JSON_READ
+               if(*index < current_strlen) {
+                       if(string[*index] == ',' || string[*index] == '}') {
+                               status = 0;
+                               set = 1;
+                       }
+               }
+               if(!set)
+                       status = _json_parse_string(string, index, value_obj, current_strlen);
+
+#else
+                status = _json_parse_string(string, index, value_obj, current_strlen);
+#endif
+
+               if(status) return status;
+
+               /* put the data into the object and continue */
+               jsonObjectSetKey(obj, key, value_obj);
+               jsonObjectFree(key_obj);
+               in_parse = 1;
+               set = 0;
+       }
+
+       if(!done)
+               return json_handle_error(string, index,
+                       "json_parse_json_object(): object not closed");
+
+       return 0;
+}
+
+
+
+/* when done, index will point to the character after the closing quote */
+int json_parse_json_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
+       if( ! string || ! index || *index >= current_strlen ) return -1;
+
+       int in_escape = 0;      
+       int done = 0;
+       growing_buffer* buf = buffer_init(64);
+
+       while(*index < current_strlen) {
+
+               char c = string[*index]; 
+
+               switch(c) {
+
+                       case '\\':
+                               if(in_escape) {
+                                       buffer_add(buf, "\\");
+                                       in_escape = 0;
+                               } else 
+                                       in_escape = 1;
+                               break;
+
+                       case '"':
+                               if(in_escape) {
+                                       buffer_add(buf, "\"");
+                                       in_escape = 0;
+                               } else 
+                                       done = 1;
+                               break;
+
+                       case 't':
+                               if(in_escape) {
+                                       buffer_add(buf,"\t");
+                                       in_escape = 0;
+                               } else 
+                                       buffer_add_char(buf, c);
+                               break;
+
+                       case 'b':
+                               if(in_escape) {
+                                       buffer_add(buf,"\b");
+                                       in_escape = 0;
+                               } else 
+                                       buffer_add_char(buf, c);
+                               break;
+
+                       case 'f':
+                               if(in_escape) {
+                                       buffer_add(buf,"\f");
+                                       in_escape = 0;
+                               } else 
+                                       buffer_add_char(buf, c);
+                               break;
+
+                       case 'r':
+                               if(in_escape) {
+                                       buffer_add(buf,"\r");
+                                       in_escape = 0;
+                               } else 
+                                       buffer_add_char(buf, c);
+                               break;
+
+                       case 'n':
+                               if(in_escape) {
+                                       buffer_add(buf,"\n");
+                                       in_escape = 0;
+                               } else 
+                                       buffer_add_char(buf, c);
+                               break;
+
+                       case 'u':
+                               if(in_escape) {
+                                       (*index)++;
+
+                                       if(*index >= (current_strlen - 4)) {
+                                               buffer_free(buf);
+                                               return json_handle_error(string, index,
+                                                       "json_parse_json_string(): truncated escaped unicode"); }
+
+                                       char buff[5];
+                                       memset(buff,0,5);
+                                       memcpy(buff, string + (*index), 4);
+
+
+                                       /* ----------------------------------------------------------------------- */
+                                       /* ----------------------------------------------------------------------- */
+                                       /* The following chunk was borrowed with permission from 
+                                               json-c http://oss.metaparadigm.com/json-c/ */
+                                       unsigned char utf_out[3];
+                                       memset(utf_out,0,3);
+
+                                       #define hexdigit(x) ( ((x) <= '9') ? (x) - '0' : ((x) & 7) + 9)
+
+                                       unsigned int ucs_char =
+                                               (hexdigit(string[*index] ) << 12) +
+                                               (hexdigit(string[*index + 1]) << 8) +
+                                               (hexdigit(string[*index + 2]) << 4) +
+                                               hexdigit(string[*index + 3]);
+       
+                                       if (ucs_char < 0x80) {
+                                               utf_out[0] = ucs_char;
+                                               buffer_add(buf, (char*) utf_out);
+
+                                       } else if (ucs_char < 0x800) {
+                                               utf_out[0] = 0xc0 | (ucs_char >> 6);
+                                               utf_out[1] = 0x80 | (ucs_char & 0x3f);
+                                               buffer_add(buf, (char*) utf_out);
+
+                                       } else {
+                                               utf_out[0] = 0xe0 | (ucs_char >> 12);
+                                               utf_out[1] = 0x80 | ((ucs_char >> 6) & 0x3f);
+                                               utf_out[2] = 0x80 | (ucs_char & 0x3f);
+                                               buffer_add(buf, (char*) utf_out);
+                                       }
+                                       /* ----------------------------------------------------------------------- */
+                                       /* ----------------------------------------------------------------------- */
+
+                                       (*index) += 3;
+                                       in_escape = 0;
+
+                               } else {
+
+                                       buffer_add_char(buf, c);
+                               }
+
+                               break;
+
+                       default:
+                               buffer_add_char(buf, c);
+               }
+
+               (*index)++;
+               if(done) break;
+       }
+
+       jsonObjectSetString(obj, buf->buf);
+       buffer_free(buf);
+       return 0;
+}
+
+
+void json_eat_ws(char* string, unsigned long* index, int eat_all, int current_strlen) {
+       if( ! string || ! index ) return;
+       if(*index >= current_strlen)
+               return;
+
+       if( eat_all ) { /* removes newlines, etc */
+               while(string[*index] == ' '     || 
+                               string[*index] == '\n'  ||
+                               string[*index] == '\t') 
+                       (*index)++;
+       }
+
+       else    
+               while(string[*index] == ' ') (*index)++;
+}
+
+
+/* index should be at the '*' character at the beginning of the comment.
+ * when done, index will point to the first character after the final /
+ */
+int json_eat_comment(char* string, unsigned long* index, char** buffer, int parse_class, int current_strlen) {
+       if( ! string || ! index || *index >= current_strlen ) return -1;
+       
+
+       if(string[*index] != '*' && string[*index] != '/' )
+               return json_handle_error(string, index, 
+                       "json_eat_comment(): invalid character after /");
+
+       /* chop out any // style comments */
+       if(string[*index] == '/') {
+               (*index)++;
+               char c = string[*index];
+               while(*index < current_strlen) {
+                       (*index)++;
+                       if(c == '\n') 
+                               return 0;
+                       c = string[*index];
+               }
+               return 0;
+       }
+
+       (*index)++;
+
+       int on_star                     = 0; /* true if we just saw a '*' character */
+
+       /* we're just past the '*' */
+       if(!parse_class) { /* we're not concerned with class hints */
+               while(*index < current_strlen) {
+                       if(string[*index] == '/') {
+                               if(on_star) {
+                                       (*index)++;
+                                       return 0;
+                               }
+                       }
+
+                       if(string[*index] == '*') on_star = 1;
+                       else on_star = 0;
+
+                       (*index)++;
+               }
+               return 0;
+       }
+
+
+
+       growing_buffer* buf = buffer_init(64);
+
+       int first_dash          = 0;
+       int second_dash = 0;
+       int third_dash          = 0;
+       int fourth_dash = 0;
+
+       int in_hint                     = 0;
+       int done                                = 0;
+
+       /*--S hint--*/   /* <-- Hints  look like this */
+       /*--E hint--*/
+
+       while(*index < current_strlen) {
+               char c = string[*index];
+
+               switch(c) {
+
+                       case '-':
+                               on_star = 0;
+                               if(third_dash)                  fourth_dash = 1;
+                               else if(in_hint)                third_dash      = 1;
+                               else if(first_dash)     second_dash = 1;
+                               else                                            first_dash = 1;
+                               break;
+
+                       case 'S':
+                               on_star = 0;
+                               if(second_dash && !in_hint) {
+                                       (*index)++;
+                                       json_eat_ws(string, index, 1, current_strlen);
+                                       (*index)--; /* this will get incremented at the bottom of the loop */
+                                       in_hint = 1;
+                                       break;
+                               } 
+
+                               if(second_dash && in_hint) {
+                                       buffer_add_char(buf, c);
+                                       break;
+                               }
+
+                       case 'E':
+                               on_star = 0;
+                               if(second_dash && !in_hint) {
+                                       (*index)++;
+                                       json_eat_ws(string, index, 1, current_strlen);
+                                       (*index)--; /* this will get incremented at the bottom of the loop */
+                                       in_hint = 1;
+                                       break;
+                               }
+
+                               if(second_dash && in_hint) {
+                                       buffer_add_char(buf, c);
+                                       break;
+                               }
+
+                       case '*':
+                               on_star = 1;
+                               break;
+
+                       case '/':
+                               if(on_star) 
+                                       done = 1;
+                               else
+                               on_star = 0;
+                               break;
+
+                       default:
+                               on_star = 0;
+                               if(in_hint)
+                                       buffer_add_char(buf, c);
+               }
+
+               (*index)++;
+               if(done) break;
+       }
+
+       if( buf->n_used > 0 && buffer)
+               *buffer = buffer_data(buf);
+
+       buffer_free(buf);
+       return 0;
+}
+
+int json_handle_error(char* string, unsigned long* index, char* err_msg) {
+
+       char buf[60];
+       memset(buf, 0, 60);
+
+       if(*index > 30)
+               strncpy( buf, string + (*index - 30), 59 );
+       else
+               strncpy( buf, string, 59 );
+
+       fprintf(stderr, 
+                       "\nError parsing json string at charracter %c "
+                       "(code %d) and index %ld\nString length: %d\nMsg:\t%s\nNear:\t%s\nFull String:\t%s\n", 
+                       string[*index], string[*index], *index, current_strlen, err_msg, buf, string );
+
+       return -1;
+}
+
+
+jsonObject* legacy_jsonParseFile( const char* filename ) {
+       return json_parse_file( filename );
+}
+       
+jsonObject* json_parse_file(const char* filename) {
+       if(!filename) return NULL;
+       char* data = file_to_string(filename);
+       jsonObject* o = json_parse_string(data);
+       free(data);
+       return o;
+}
+
+
+
+char* legacy_jsonObjectToJSON( const jsonObject* obj ) {
+
+       if(obj == NULL) return strdup("null");
+
+       growing_buffer* buf = buffer_init(64);
+
+       /* add class hints if we have a class name */
+       if(obj->classname) {
+               buffer_add(buf,"/*--S ");
+               buffer_add(buf,obj->classname);
+               buffer_add(buf, "--*/");
+       }
+
+       switch( obj->type ) {
+
+               case JSON_BOOL: 
+                       if(obj->value.b) buffer_add(buf, "true"); 
+                       else buffer_add(buf, "false"); 
+                       break;
+
+               case JSON_NUMBER: {
+                       double x = obj->value.n;
+
+                       /* if the number does not need to be a double,
+                               turn it into an int on the way out */
+                       if( x == (int) x ) {
+                               INT_TO_STRING((int)x);  
+                               buffer_add(buf, INTSTR);
+
+                       } else {
+                               DOUBLE_TO_STRING(x);
+                               buffer_add(buf, DOUBLESTR);
+                       }
+                       break;
+               }
+
+               case JSON_NULL:
+                       buffer_add(buf, "null");
+                       break;
+
+               case JSON_STRING:
+                       buffer_add(buf, "\"");
+                       char* data = obj->value.s;
+                       int len = strlen(data);
+                       
+                       char* output = uescape(data, len, 1);
+                       buffer_add(buf, output);
+                       free(output);
+                       buffer_add(buf, "\"");
+                       break;
+
+               case JSON_ARRAY:
+                       buffer_add(buf, "[");
+                       int i;
+                       for( i = 0; i!= obj->size; i++ ) {
+                               const jsonObject* x = jsonObjectGetIndex(obj,i);
+                               char* data = legacy_jsonObjectToJSON(x);
+                               buffer_add(buf, data);
+                               free(data);
+                               if(i != obj->size - 1)
+                                       buffer_add(buf, ",");
+                       }
+                       buffer_add(buf, "]");
+                       break;  
+
+               case JSON_HASH:
+       
+                       buffer_add(buf, "{");
+                       jsonIterator* itr = jsonNewIterator(obj);
+                       jsonObject* tmp;
+       
+                       while( (tmp = jsonIteratorNext(itr)) ) {
+
+                               buffer_add(buf, "\"");
+
+                               char* key = itr->key;
+                               int len = strlen(key);
+                               char* output = uescape(key, len, 1);
+                               buffer_add(buf, output);
+                               free(output);
+
+                               buffer_add(buf, "\":");
+                               char* data =  legacy_jsonObjectToJSON(tmp);
+                               buffer_add(buf, data);
+                               if(jsonIteratorHasNext(itr))
+                                       buffer_add(buf, ",");
+                               free(data);
+                       }
+
+                       jsonIteratorFree(itr);
+                       buffer_add(buf, "}");
+                       break;
+               
+                       default:
+                               fprintf(stderr, "Unknown object type %d\n", obj->type);
+                               break;
+                               
+       }
+
+       /* close out the object hint */
+       if(obj->classname) {
+               buffer_add(buf, "/*--E ");
+               buffer_add(buf, obj->classname);
+               buffer_add(buf, "--*/");
+       }
+
+       char* data = buffer_data(buf);
+       buffer_free(buf);
+       return data;
+}
+
+
+
+static jsonObjectNode* makeNode(jsonObject* obj, unsigned long index, char* key) {
+    jsonObjectNode* node = safe_malloc(sizeof(jsonObjectNode));
+    node->item = obj;
+    node->index = index;
+    node->key = key;
+    return node;
+}
+
+jsonObjectIterator* jsonNewObjectIterator(const jsonObject* obj) {
+       if(!obj) return NULL;
+       jsonObjectIterator* itr = safe_malloc(sizeof(jsonObjectIterator));
+    itr->iterator = jsonNewIterator(obj);
+       itr->obj = obj;
+    itr->done = 0;
+    itr->current = NULL;
+       return itr;
+}
+
+jsonObjectNode* jsonObjectIteratorNext( jsonObjectIterator* itr ) {
+       if(itr == NULL || itr->done) return NULL;
+
+    if(itr->current) free(itr->current);
+    jsonObject* next = jsonIteratorNext(itr->iterator);
+    if(next == NULL) {
+        itr->current = NULL;
+        itr->done = 1;
+        return NULL;
+    }
+    itr->current = makeNode(next, itr->iterator->index, itr->iterator->key);
+    return itr->current;
+}
+
+void jsonObjectIteratorFree(jsonObjectIterator* iter) { 
+    if(iter->current) free(iter->current);
+    jsonIteratorFree(iter->iterator);
+       free(iter);
+}
+
+int jsonObjectIteratorHasNext(const jsonObjectIterator* itr) {
+       return (itr && itr->current);
+}
+
+
index 3197718..ca8393f 100644 (file)
@@ -8,18 +8,11 @@ string_array* init_string_array(int size) {
        if(size > STRING_ARRAY_MAX_SIZE)
                osrfLogError( OSRF_LOG_MARK, "init_string_array size is too large");
 
-       /*
-       string_array* arr = 
-               (string_array*) safe_malloc(sizeof(string_array));
-               */
        string_array* arr;
        OSRF_MALLOC( arr, sizeof(string_array));
-
-       //arr->array = (char**) safe_malloc(size * sizeof(char*));
-       OSRF_MALLOC(arr->array, size * sizeof(char*));
-
+    arr->list = osrfNewListSize(size);
+    osrfListSetDefaultFree(arr->list);
        arr->size = 0;
-       arr->arr_size = size;
        return arr;
 }
 
@@ -30,65 +23,39 @@ void osrfStringArrayAdd(osrfStringArray* arr, char* string) {
 
 void string_array_add(string_array* arr, char* str) {
        if(arr == NULL || str == NULL ) return;
-       if( strlen(str) < 1 ) return;
-
-       arr->size++;
-
        if( arr->size > STRING_ARRAY_MAX_SIZE ) 
                osrfLogError( OSRF_LOG_MARK, "string_array_add size is too large");
-
-       /* if necessary, double capacity */
-       if(arr->size >= arr->arr_size) {
-               arr->arr_size *= 2;
-               //char** tmp = (char**) safe_malloc(arr->arr_size * sizeof(char*));
-               char** tmp;
-               OSRF_MALLOC( tmp, arr->arr_size * sizeof(char*));
-               int i;
-
-               /* copy the string pointers over */
-               for( i = 0; i!= arr->size; i++ ) 
-                       tmp[i] = arr->array[i];
-
-               free(arr->array);
-               arr->array = tmp;
-       }
-
-       arr->array[arr->size - 1] = strdup(str);
+    osrfListPush(arr->list, strdup(str));
+    arr->size = arr->list->size;
 }
 
 char* osrfStringArrayGetString(osrfStringArray* arr, int index) {
-       return string_array_get_string(arr, index);
+    if(!arr) return NULL;
+    return OSRF_LIST_GET_INDEX(arr->list, index);
 }
 
 char* string_array_get_string(string_array* arr, int index) {
-       if(!arr || index < 0 || index >= arr->size ) return NULL;
-       return arr->array[index]; 
+    if(!arr) return NULL;
+    return OSRF_LIST_GET_INDEX(arr->list, index);
 }
 
 
 void osrfStringArrayFree(osrfStringArray* arr) {
-       string_array_destroy(arr);
+    OSRF_STRING_ARRAY_FREE(arr);
 }
 
 void string_array_destroy(string_array* arr) {
-       if(arr) {
-               int i = 0;
-               while( i < arr->size ) free(arr->array[i++]);
-               free(arr->array);
-               free(arr);
-       }
+    OSRF_STRING_ARRAY_FREE(arr);
 }
 
 
 int osrfStringArrayContains( osrfStringArray* arr, char* string ) {
        if(!(arr && string)) return 0;
-       
        int i;
-       for( i = 0; i != arr->size; i++ ) {
-               char* str = osrfStringArrayGetString(arr, i);
-               if(str) {
-                       if(!strcmp(str, string)) return 1;
-               }
+       for( i = 0; i < arr->size; i++ ) {
+        char* str = OSRF_LIST_GET_INDEX(arr->list, i);
+               if(str && !strcmp(str, string)) 
+            return 1;
        }
 
        return 0;
@@ -97,19 +64,29 @@ int osrfStringArrayContains( osrfStringArray* arr, char* string ) {
 void osrfStringArrayRemove( osrfStringArray* arr, char* tstr) {
        if(!(arr && tstr)) return;
        int i;
-       for( i = 0; i != arr->size; i++ ) {
-               char* str = osrfStringArrayGetString(arr, i);
-               if(str) {
-                       if(!strcmp(str, tstr)) {
-                               free(arr->array[i]);
-                               arr->array[i] = NULL;
-                               break;
-                       }
+    char* str;
+
+       for( i = 0; i < arr->size; i++ ) {
+        /* find and remove the string */
+        str = OSRF_LIST_GET_INDEX(arr->list, i);
+               if(str && !strcmp(str, tstr)) {
+            osrfListRemove(arr->list, i);
+                       break;
                }
        }
-       for( ; i != arr->size; i++ ) 
-               arr->array[i] = arr->array[i+1];
 
+    /* disable automatic item freeing on delete and shift
+     * items up in the array to fill in the gap
+     */
+    arr->list->freeItem = NULL;
+       for( ; i < arr->size - 1; i++ ) 
+        osrfListSet(arr->list, OSRF_LIST_GET_INDEX(arr->list, i+1) , i);
+
+    /* remove the last item since it was shifted up */
+    osrfListRemove(arr->list, i);
+
+    /* re-enable automatic item freeing in delete */
+    osrfListSetDefaultFree(arr->list);
        arr->size--;
 }
 
index b417425..c78ec48 100644 (file)
@@ -85,7 +85,79 @@ sub _json_hint_to_class {
        return $hint;
 }
 
+
+my $JSON_CLASS_KEY = '__c';
+my $JSON_PAYLOAD_KEY = '__p';
+
 sub JSON2perl {
+       my( $class, $string ) = @_;
+       my $perl = $class->rawJSON2perl($string);
+       return $class->JSONObject2Perl($perl);
+}
+
+sub perl2JSON {
+       my( $class, $obj ) = @_;
+       my $json = $class->perl2JSONObject($obj);
+       return $class->rawPerl2JSON($json);
+}
+
+sub JSONObject2Perl {
+       my $class = shift;
+       my $obj = shift;
+       my $ref = ref($obj);
+       if( $ref eq 'HASH' ) {
+               if( defined($obj->{$JSON_CLASS_KEY})) {
+                       my $cls = $obj->{$JSON_CLASS_KEY};
+            $cls =~ s/^\s+//o;
+            $cls =~ s/\s+$//o;
+                       if( $obj = $class->JSONObject2Perl($obj->{$JSON_PAYLOAD_KEY}) ) {
+                               $cls = $class->lookup_class($cls) || $cls;
+                               return bless(\$obj, $cls) unless ref($obj); 
+                               return bless($obj, $cls);
+                       }
+                       return undef;
+               }
+               $obj->{$_} = $class->JSONObject2Perl($obj->{$_}) for (keys %$obj);
+       } elsif( $ref eq 'ARRAY' ) {
+               $obj->[$_] = $class->JSONObject2Perl($obj->[$_]) for(0..scalar(@$obj) - 1);
+       }
+       return $obj;
+}
+
+sub perl2JSONObject {
+       my $class = shift;
+       my $obj = shift;
+       my $ref = ref($obj);
+
+       return $obj unless $ref;
+       my $newobj;
+
+       if( $ref eq 'HASH' ) {
+               $newobj = {};
+               $newobj->{$_} = $class->perl2JSONObject( $obj->{$_} ) for (keys %$obj);
+       } elsif( $ref eq 'ARRAY' ) {
+               $newobj = [];
+               $newobj->[$_] = $class->perl2JSONObject( $obj->[$_] ) for(0..scalar(@$obj) - 1 );
+       } elsif( $ref ) {
+               if(UNIVERSAL::isa($obj, 'HASH')) {
+                       $newobj = {};
+                       $newobj->{$_} = $class->perl2JSONObject( $obj->{$_} ) for (keys %$obj);
+                       bless( $newobj, ref($obj) );
+                       #bless($obj, 'HASH'); # so our parser won't add the hints
+               } elsif(UNIVERSAL::isa($obj, 'ARRAY')) {
+                       $newobj = [];
+                       $newobj->[$_] = $class->perl2JSONObject( $obj->[$_] ) for(0..scalar(@$obj) - 1);
+                       bless( $newobj, ref($obj) );
+                       #bless($obj, 'ARRAY'); # so our parser won't add the hints
+               }
+               $ref = $class->lookup_hint($ref) || $ref;
+               $newobj = { $JSON_CLASS_KEY => $ref, $JSON_PAYLOAD_KEY => $newobj };
+       } 
+       return $newobj; 
+}
+
+
+sub rawJSON2perl {
        my $class = shift;
        local $_ = shift;
 
@@ -97,8 +169,8 @@ sub JSON2perl {
        s/\\u([0-9a-fA-F]{4})/chr(hex($1))/esog;
 
        # handle class blessings
-       s/\/\*--\s*S\w*?\s+\S+\s*--\*\// bless(/sog;
-       s/(\]|\}|")\s*\/\*--\s*E\w*?\s+(\S+)\s*--\*\//$1 => _json_hint_to_class("$1", "$2")) /sog;
+#      s/\/\*--\s*S\w*?\s+\S+\s*--\*\// bless(/sog;
+#      s/(\]|\}|")\s*\/\*--\s*E\w*?\s+(\S+)\s*--\*\//$1 => _json_hint_to_class("$1", "$2")) /sog;
 
        my $re = qr/((?<!\\)"(?>(?<=\\)"|[^"])*(?<!\\)")/;
        # Grab strings...
@@ -112,17 +184,18 @@ sub JSON2perl {
        s/:/ => /sog;
 
        # Do numbers...
-       #s/\b(-?\d+\.?\d*)\b/ OpenSRF::Utils::JSON::number::new($1) /sog;
+       #s/\b(-?\d+\.?\d*)\b/ JSON::number::new($1) /sog;
 
        # Change javascript stuff to perl...
        s/null/ undef /sog;
-       s/true/ bless( {}, "OpenSRF::Utils::JSON::bool::true") /sog;
-       s/false/ bless( {}, "OpenSRF::Utils::JSON::bool::false") /sog;
+       s/true/ bless( {}, "JSON::bool::true") /sog;
+       s/false/ bless( {}, "JSON::bool::false") /sog;
 
        my $ret;
        return eval '$ret = '.$_;
 }
 
+
 my $_json_index;
 sub ___JSON2perl {
        my $class = shift;
@@ -638,7 +711,7 @@ sub old_JSON2perl {
                } elsif ($element =~ /^\/\*/) {
                        next;
                } elsif ($element =~ /^\d/) {
-                       $output .= "do { OpenSRF::Utils::JSON::number::new($element) }";
+                       $output .= "do { JSON::number::new($element) }";
                        next;
                } elsif ($element eq '{' or $element eq '[') {
                        $casting_depth++;
@@ -654,10 +727,10 @@ sub old_JSON2perl {
                        $output .= ' => ';
                        next;
                } elsif ($element eq 'true') {
-                       $output .= 'bless( {}, "OpenSRF::Utils::JSON::bool::true")';
+                       $output .= 'bless( {}, "JSON::bool::true")';
                        next;
                } elsif ($element eq 'false') {
-                       $output .= 'bless( {}, "OpenSRF::Utils::JSON::bool::false")';
+                       $output .= 'bless( {}, "JSON::bool::false")';
                        next;
                }
                
@@ -667,26 +740,28 @@ sub old_JSON2perl {
        return eval $output;
 }
 
-sub perl2JSON {
+
+sub rawPerl2JSON {
        my ($class, $perl, $strict) = @_;
 
        my $output = '';
        if (!defined($perl)) {
                $output = '' if $strict;
                $output = 'null' unless $strict;
-       } elsif (ref($perl) and ref($perl) =~ /^OpenSRF::Utils::JSON/) {
+       } elsif (ref($perl) and ref($perl) =~ /^JSON/) {
                $output .= $perl;
-       } elsif ( ref($perl) && exists($_class_map{classes}{ref($perl)}) ) {
-               $output .= '/*--S '.$_class_map{classes}{ref($perl)}{hint}.'--*/';
-               if (lc($_class_map{classes}{ref($perl)}{type}) eq 'hash') {
-                       my %hash =  %$perl;
-                       $output .= perl2JSON(undef,\%hash, $strict);
-               } elsif (lc($_class_map{classes}{ref($perl)}{type}) eq 'array') {
-                       my @array =  @$perl;
-                       $output .= perl2JSON(undef,\@array, $strict);
-               }
-               $output .= '/*--E '.$_class_map{classes}{ref($perl)}{hint}.'--*/';
-       } elsif (ref($perl) and ref($perl) =~ /HASH/) {
+#      } elsif ( ref($perl) && exists($_class_map{classes}{ref($perl)}) ) {
+#              $output .= '/*--S '.$_class_map{classes}{ref($perl)}{hint}.'--*/';
+#              if (lc($_class_map{classes}{ref($perl)}{type}) eq 'hash') {
+#                      my %hash =  %$perl;
+#                      $output .= rawPerl2JSON(undef,\%hash, $strict);
+#              } elsif (lc($_class_map{classes}{ref($perl)}{type}) eq 'array') {
+#                      my @array =  @$perl;
+#                      $output .= rawPerl2JSON(undef,\@array, $strict);
+#              }
+#              $output .= '/*--E '.$_class_map{classes}{ref($perl)}{hint}.'--*/';
+#      } elsif (ref($perl) and ref($perl) =~ /HASH/) {
+       } elsif (UNIVERSAL::isa($perl, 'HASH')) {
                $output .= '{';
                my $c = 0;
                for my $key (sort keys %$perl) {
@@ -701,27 +776,28 @@ sub perl2JSON {
                        $outkey =~ s/\n/\\n/sgo;
                        $outkey =~ s/([\x{0080}-\x{fffd}])/sprintf('\u%0.4x',ord($1))/sgoe;
 
-                       $output .= '"'.$outkey.'":'. perl2JSON(undef,$$perl{$key}, $strict);
+                       $output .= '"'.$outkey.'":'. rawPerl2JSON(undef,$$perl{$key}, $strict);
                        $c++;
                }
                $output .= '}';
-       } elsif (ref($perl) and ref($perl) =~ /ARRAY/) {
+#      } elsif (ref($perl) and ref($perl) =~ /ARRAY/) {
+       } elsif (UNIVERSAL::isa($perl, 'ARRAY')) {
                $output .= '[';
                my $c = 0;
                for my $part (@$perl) {
                        $output .= ',' if ($c); 
                        
-                       $output .= perl2JSON(undef,$part, $strict);
+                       $output .= rawPerl2JSON(undef,$part, $strict);
                        $c++;
                }
                $output .= ']';
        } elsif (ref($perl) and ref($perl) =~ /CODE/) {
-               $output .= perl2JSON(undef,$perl->(), $strict);
+               $output .= rawPerl2JSON(undef,$perl->(), $strict);
        } elsif (ref($perl) and ("$perl" =~ /^([^=]+)=(\w+)/o)) {
                my $type = $2;
                my $name = $1;
                OpenSRF::Utils::JSON->register_class_hint(name => $name, hint => $name, type => lc($type));
-               $output .= perl2JSON(undef,$perl, $strict);
+               $output .= rawPerl2JSON(undef,$perl, $strict);
        } else {
                $perl = NFC($perl);
                $perl =~ s{\\}{\\\\}sgo;
@@ -750,7 +826,7 @@ sub perl2prettyJSON {
        if (!defined($perl)) {
                $output = "   "x$depth unless($nospace);
                $output .= 'null';
-       } elsif (ref($perl) and ref($perl) =~ /^OpenSRF::Utils::JSON/) {
+       } elsif (ref($perl) and ref($perl) =~ /^JSON/) {
                $output = "   "x$depth unless($nospace);
                $output .= $perl;
        } elsif ( ref($perl) && exists($_class_map{classes}{ref($perl)}) ) {
index e156743..f545b66 100644 (file)
@@ -1,6 +1,6 @@
 #MALLOC_CHECK_=1 # XXX debug only
 
-LDLIBS += -lxml2 -lopensrf -lobjson
+LDLIBS += -lxml2 -lopensrf 
 CFLAGS += -D_ROUTER
 
 all: opensrf_router 
index 9b04bd5..efea8b2 100644 (file)
@@ -1,6 +1,6 @@
 # if EXEC_DEFAULT is defined, then srfsh will send all unknown commands to the shell for execution
 
-LDLIBS += -lobjson -lreadline -lxml2 -lopensrf -lncurses 
+LDLIBS += -lreadline -lxml2 -lopensrf -lncurses 
 LDFLAGS        += -DEXEC_DEFAULT
 
 all: srfsh
index 6c4cb54..2d41125 100644 (file)
@@ -558,7 +558,7 @@ int send_request( char* server,
        jsonObject* params = NULL;
        if( !relay ) {
                if( buffer != NULL && buffer->n_used > 0 ) 
-                       params = json_parse_string(buffer->buf);
+                       params = jsonParseString(buffer->buf);
        } else {
                if(!last_result || ! last_result->_result_content) { 
                        printf("We're not going to call 'relay' with no result params\n");
@@ -850,7 +850,7 @@ static int do_math( int count, int style ) {
        osrf_app_session* session = osrf_app_client_session_init(  "opensrf.math" );
        osrf_app_session_connect(session);
 
-       jsonObject* params = json_parse_string("[]");
+       jsonObject* params = jsonParseString("[]");
        jsonObjectPush(params,jsonNewObject("1"));
        jsonObjectPush(params,jsonNewObject("2"));