cJSON_Utils.c 13.1 KB
Newer Older
1
#include <ctype.h>
2 3
#include <string.h>
#include <stdlib.h>
4
#include <stdio.h>
5 6
#include "cJSON_Utils.h"

M
Max Bruckner 已提交
7
static int cJSONUtils_strcasecmp(const char *s1, const char *s2)
8
{
M
Max Bruckner 已提交
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
    if (!s1)
    {
        return (s1 == s2) ? 0 : 1; /* both NULL? */
    }
    if (!s2)
    {
        return 1;
    }
    for(; tolower(*s1) == tolower(*s2); ++s1, ++s2)
    {
        if(*s1 == 0)
        {
            return 0;
        }
    }

    return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
26 27
}

28
/* JSON Pointer implementation: */
29
static int cJSONUtils_Pstrcasecmp(const char *a, const char *e)
30
{
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
    if (!a || !e)
    {
        return (a == e) ? 0 : 1; /* both NULL? */
    }
    for (; *a && *e && (*e != '/'); a++, e++) /* compare until next '/' */
    {
        if (*e == '~')
        {
            /* check for escaped '~' (~0) and '/' (~1) */
            if (!((e[1] == '0') && (*a == '~')) && !((e[1] == '1') && (*a == '/')))
            {
                /* invalid escape sequence or wrong character in *a */
                return 1;
            }
            else
            {
                e++;
            }
        }
        else if (tolower(*a) != tolower(*e))
        {
            return 1;
        }
    }
    if (((*e != 0) && (*e != '/')) != (*a != 0))
    {
        /* one string has ended, the other not */
        return 1;
    }

    return 0;
62 63
}

64 65 66 67 68 69 70 71 72 73 74 75 76
static int cJSONUtils_PointerEncodedstrlen(const char *s)
{
    int l = 0;
    for (; *s; s++, l++)
    {
        if ((*s == '~') || (*s == '/'))
        {
            l++;
        }
    }

    return l;
}
77

78
static void cJSONUtils_PointerEncodedstrcpy(char *d, const char *s)
79
{
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
    for (; *s; s++)
    {
        if (*s == '/')
        {
            *d++ = '~';
            *d++ = '1';
        }
        else if (*s == '~')
        {
            *d++ = '~';
            *d++ = '0';
        }
        else
        {
            *d++ = *s;
        }
    }

    *d = '\0';
99 100 101 102
}

char *cJSONUtils_FindPointerFromObjectTo(cJSON *object,cJSON *target)
{
103 104
	int type=object->type,c=0;cJSON *obj=0;

105 106
	if (object==target) return strdup("");

107
	for (obj=object->child;obj;obj=obj->next,c++)
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
	{
		char *found=cJSONUtils_FindPointerFromObjectTo(obj,target);
		if (found)
		{
			if (type==cJSON_Array)
			{
				char *ret=(char*)malloc(strlen(found)+23);
				sprintf(ret,"/%d%s",c,found);
				free(found);
				return ret;
			}
			else if (type==cJSON_Object)
			{
				char *ret=(char*)malloc(strlen(found)+cJSONUtils_PointerEncodedstrlen(obj->string)+2);
				*ret='/';cJSONUtils_PointerEncodedstrcpy(ret+1,obj->string);
				strcat(ret,found);
				free(found);
				return ret;
			}
			free(found);
			return 0;
		}
	}
	return 0;
}

134 135
cJSON *cJSONUtils_GetPointer(cJSON *object,const char *pointer)
{
136
	while (*pointer++=='/' && object)
137 138 139
	{
		if (object->type==cJSON_Array)
		{
140
			int which=0; while (*pointer>='0' && *pointer<='9') which=(10*which) + *pointer++ - '0';
141 142 143 144 145
			if (*pointer && *pointer!='/') return 0;
			object=cJSON_GetArrayItem(object,which);
		}
		else if (object->type==cJSON_Object)
		{
146
			object=object->child;	while (object && cJSONUtils_Pstrcasecmp(object->string,pointer)) object=object->next;	/* GetObjectItem. */
147
			while (*pointer && *pointer!='/') pointer++;
148 149 150 151 152 153
		}
		else return 0;
	}
	return object;
}

154
/* JSON Patch implementation. */
155 156 157
static void cJSONUtils_InplaceDecodePointerString(char *string)
{
	char *s2=string;
158
	for (;*string;s2++,string++) *s2=(*string!='~')?(*string):((*(++string)=='0')?'~':'/');
159 160 161 162 163
	*s2=0;
}

static cJSON *cJSONUtils_PatchDetach(cJSON *object,const char *path)
{
164
	char *parentptr=0,*childptr=0;cJSON *parent=0,*ret=0;
165 166 167 168 169

	parentptr=strdup(path);	childptr=strrchr(parentptr,'/');	if (childptr) *childptr++=0;
	parent=cJSONUtils_GetPointer(object,parentptr);
	cJSONUtils_InplaceDecodePointerString(childptr);

170
	if (!parent) ret=0;	/* Couldn't find object to remove child from. */
171 172 173 174 175 176
	else if (parent->type==cJSON_Array)		ret=cJSON_DetachItemFromArray(parent,atoi(childptr));
	else if (parent->type==cJSON_Object)	ret=cJSON_DetachItemFromObject(parent,childptr);
	free(parentptr);
	return ret;
}

177 178
static int cJSONUtils_Compare(cJSON *a,cJSON *b)
{
179
	if (a->type!=b->type)	return -1;	/* mismatched type. */
180 181
	switch (a->type)
	{
182 183
	case cJSON_Number:	return (a->valueint!=b->valueint || a->valuedouble!=b->valuedouble)?-2:0;	/* numeric mismatch. */
	case cJSON_String:	return (strcmp(a->valuestring,b->valuestring)!=0)?-3:0;						/* string mismatch. */
184
	case cJSON_Array:	for (a=a->child,b=b->child;a && b;a=a->next,b=b->next)	{int err=cJSONUtils_Compare(a,b);if (err) return err;}
185
						return (a || b)?-4:0;	/* array size mismatch. */
186
	case cJSON_Object:
187 188 189 190
						cJSONUtils_SortObject(a);
						cJSONUtils_SortObject(b);
						a=a->child,b=b->child;
						while (a && b)
191
						{
192 193 194 195
							int err;
							if (cJSONUtils_strcasecmp(a->string,b->string))	return -6;	/* missing member */
							err=cJSONUtils_Compare(a,b);if (err) return err;
							a=a->next,b=b->next;
196
						}
197 198
						return (a || b)?-5:0;	/* object length mismatch */

199 200 201 202 203
	default:			break;
	}
	return 0;
}

204 205
static int cJSONUtils_ApplyPatch(cJSON *object,cJSON *patch)
{
206 207
	cJSON *op=0,*path=0,*value=0,*parent=0;int opcode=0;char *parentptr=0,*childptr=0;

208 209
	op=cJSON_GetObjectItem(patch,"op");
	path=cJSON_GetObjectItem(patch,"path");
210
	if (!op || !path) return 2;	/* malformed patch. */
211 212 213 214 215 216

	if		(!strcmp(op->valuestring,"add"))	opcode=0;
	else if (!strcmp(op->valuestring,"remove")) opcode=1;
	else if (!strcmp(op->valuestring,"replace"))opcode=2;
	else if (!strcmp(op->valuestring,"move"))	opcode=3;
	else if (!strcmp(op->valuestring,"copy"))	opcode=4;
217
	else if (!strcmp(op->valuestring,"test"))	return cJSONUtils_Compare(cJSONUtils_GetPointer(object,path->valuestring),cJSON_GetObjectItem(patch,"value"));
218
	else return 3; /* unknown opcode. */
219

220
	if (opcode==1 || opcode==2)	/* Remove/Replace */
221
	{
222 223
		cJSON_Delete(cJSONUtils_PatchDetach(object,path->valuestring));	/* Get rid of old. */
		if (opcode==1) return 0;	/* For Remove, this is job done. */
224 225
	}

226
	if (opcode==3 || opcode==4)	/* Copy/Move uses "from". */
227
	{
228
		cJSON *from=cJSON_GetObjectItem(patch,"from");	if (!from) return 4; /* missing "from" for copy/move. */
229 230 231

		if (opcode==3) value=cJSONUtils_PatchDetach(object,from->valuestring);
		if (opcode==4) value=cJSONUtils_GetPointer(object,from->valuestring);
232
		if (!value) return 5; /* missing "from" for copy/move. */
233
		if (opcode==4) value=cJSON_Duplicate(value,1);
234
		if (!value) return 6; /* out of memory for copy/move. */
235
	}
236
	else	/* Add/Replace uses "value". */
237 238
	{
		value=cJSON_GetObjectItem(patch,"value");
239
		if (!value) return 7; /* missing "value" for add/replace. */
240
		value=cJSON_Duplicate(value,1);
241
		if (!value) return 8; /* out of memory for add/replace. */
242 243
	}
		
244
	/* Now, just add "value" to "path". */
245 246 247 248 249

	parentptr=strdup(path->valuestring);	childptr=strrchr(parentptr,'/');	if (childptr) *childptr++=0;
	parent=cJSONUtils_GetPointer(object,parentptr);
	cJSONUtils_InplaceDecodePointerString(childptr);

250
	/* add, remove, replace, move, copy, test. */
M
Max Bruckner 已提交
251
	if (!parent) {free(parentptr); cJSON_Delete(value); return 9;}	/* Couldn't find object to add to. */
252 253 254 255 256 257 258 259 260 261
	else if (parent->type==cJSON_Array)
	{
		if (!strcmp(childptr,"-"))	cJSON_AddItemToArray(parent,value);
		else						cJSON_InsertItemInArray(parent,atoi(childptr),value);
	}
	else if (parent->type==cJSON_Object)
	{
		cJSON_DeleteItemFromObject(parent,childptr);
		cJSON_AddItemToObject(parent,childptr,value);
	}
M
Max Bruckner 已提交
262 263 264 265
	else
	{
		cJSON_Delete(value);
	}
266 267 268 269 270 271 272 273
	free(parentptr);
	return 0;
}


int cJSONUtils_ApplyPatches(cJSON *object,cJSON *patches)
{
	int err;
M
Max Bruckner 已提交
274
	if (patches->type!=cJSON_Array) return 1;	/* malformed patches. */
275 276 277 278 279 280 281 282
	if (patches) patches=patches->child;
	while (patches)
	{
		if ((err=cJSONUtils_ApplyPatch(object,patches))) return err;
		patches=patches->next;
	}
	return 0;
}
283

284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
static void cJSONUtils_GeneratePatch(cJSON *patches,const char *op,const char *path,const char *suffix,cJSON *val)
{
	cJSON *patch=cJSON_CreateObject();
	cJSON_AddItemToObject(patch,"op",cJSON_CreateString(op));
	if (suffix)
	{
		char *newpath=(char*)malloc(strlen(path)+cJSONUtils_PointerEncodedstrlen(suffix)+2);
		cJSONUtils_PointerEncodedstrcpy(newpath+sprintf(newpath,"%s/",path),suffix);
		cJSON_AddItemToObject(patch,"path",cJSON_CreateString(newpath));
		free(newpath);
	}
	else	cJSON_AddItemToObject(patch,"path",cJSON_CreateString(path));
	if (val) cJSON_AddItemToObject(patch,"value",cJSON_Duplicate(val,1));
	cJSON_AddItemToArray(patches,patch);
}

void cJSONUtils_AddPatchToArray(cJSON *array,const char *op,const char *path,cJSON *val)	{cJSONUtils_GeneratePatch(array,op,path,0,val);}

static void cJSONUtils_CompareToPatch(cJSON *patches,const char *path,cJSON *from,cJSON *to)
{
	if (from->type!=to->type)	{cJSONUtils_GeneratePatch(patches,"replace",path,0,to);	return;	}
	
	switch (from->type)
	{
	case cJSON_Number:	
		if (from->valueint!=to->valueint || from->valuedouble!=to->valuedouble)
			cJSONUtils_GeneratePatch(patches,"replace",path,0,to);
		return;
						
	case cJSON_String:	
		if (strcmp(from->valuestring,to->valuestring)!=0)
			cJSONUtils_GeneratePatch(patches,"replace",path,0,to);
		return;

	case cJSON_Array:
	{
320
		int c;char *newpath=(char*)malloc(strlen(path)+23);	/* Allow space for 64bit int. */
321 322 323 324 325 326 327 328 329 330
		for (c=0,from=from->child,to=to->child;from && to;from=from->next,to=to->next,c++){
										sprintf(newpath,"%s/%d",path,c);	cJSONUtils_CompareToPatch(patches,newpath,from,to);
		}
		for (;from;from=from->next,c++)	{sprintf(newpath,"%d",c);	cJSONUtils_GeneratePatch(patches,"remove",path,newpath,0);	}
		for (;to;to=to->next,c++)		cJSONUtils_GeneratePatch(patches,"add",path,"-",to);
		free(newpath);
		return;
	}

	case cJSON_Object:
331
	{
332 333 334 335 336 337
		cJSON *a,*b;
		cJSONUtils_SortObject(from);
		cJSONUtils_SortObject(to);
		
		a=from->child,b=to->child;
		while (a || b)
338
		{
339 340
			int diff=(!a)?1:(!b)?-1:cJSONUtils_strcasecmp(a->string,b->string);
			if (!diff)
341 342 343
			{
				char *newpath=(char*)malloc(strlen(path)+cJSONUtils_PointerEncodedstrlen(a->string)+2);
				cJSONUtils_PointerEncodedstrcpy(newpath+sprintf(newpath,"%s/",path),a->string);
344
				cJSONUtils_CompareToPatch(patches,newpath,a,b);
345
				free(newpath);
346 347
				a=a->next;
				b=b->next;
348
			}
349 350
			else if (diff<0)	{cJSONUtils_GeneratePatch(patches,"remove",path,a->string,0);	a=a->next;}
			else				{cJSONUtils_GeneratePatch(patches,"add",path,b->string,b);		b=b->next;}
351 352
		}
		return;
353
	}
354 355 356 357 358 359 360 361 362 363 364 365 366

	default:			break;
	}
}


cJSON* cJSONUtils_GeneratePatches(cJSON *from,cJSON *to)
{
	cJSON *patches=cJSON_CreateArray();	
	cJSONUtils_CompareToPatch(patches,"",from,to);
	return patches;
}

367 368 369 370 371 372

static cJSON *cJSONUtils_SortList(cJSON *list)
{
	cJSON *first=list,*second=list,*ptr=list;

	if (!list || !list->next) return list;	/* One entry is sorted already. */
373 374 375
	
	while (ptr && ptr->next && cJSONUtils_strcasecmp(ptr->string,ptr->next->string)<0) ptr=ptr->next;	/* Test for list sorted. */
	if (!ptr || !ptr->next) return list;	/* Leave sorted lists unmodified. */
D
Dave Gamble 已提交
376
	ptr=list;
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406

	while (ptr) {second=second->next;ptr=ptr->next;if (ptr) ptr=ptr->next;}	/* Walk two pointers to find the middle. */
	if (second && second->prev) second->prev->next=0;	/* Split the lists */

	first=cJSONUtils_SortList(first);	/* Recursively sort the sub-lists. */
	second=cJSONUtils_SortList(second);
	list=ptr=0;

	while (first && second)	/* Merge the sub-lists */
	{		
		if (cJSONUtils_strcasecmp(first->string,second->string)<0)
		{
			if (!list) list=ptr=first;
			else	{ptr->next=first;first->prev=ptr;ptr=first;}
			first=first->next;
		}
		else 
		{
			if (!list) list=ptr=second;
			else	{ptr->next=second;second->prev=ptr;ptr=second;}
			second=second->next;
		}
	}
	if (first)	{	if (!list) return first;	ptr->next=first;	first->prev=ptr;	}	/* Append any tails. */
	if (second)	{	if (!list) return second;	ptr->next=second;	second->prev=ptr;	}

	return list;
}

void cJSONUtils_SortObject(cJSON *object)	{object->child=cJSONUtils_SortList(object->child);}
407

408 409
cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch)
{
410 411
	if (!patch || patch->type != cJSON_Object) {cJSON_Delete(target);return cJSON_Duplicate(patch,1);}
	if (!target || target->type != cJSON_Object) {cJSON_Delete(target);target=cJSON_CreateObject();}
412

413 414 415 416 417 418 419 420 421 422 423 424 425
	patch=patch->child;
	while (patch)
	{
		if (patch->type == cJSON_NULL) cJSON_DeleteItemFromObject(target,patch->string);
		else
		{
			cJSON *replaceme=cJSON_DetachItemFromObject(target,patch->string);
			cJSON_AddItemToObject(target,patch->string,cJSONUtils_MergePatch(replaceme,patch));
		}
		patch=patch->next;
	}
	return target;
}
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456

cJSON *cJSONUtils_GenerateMergePatch(cJSON *from,cJSON *to)
{
	cJSON *patch=0;
	if (!to) return cJSON_CreateNull();
	if (to->type!=cJSON_Object || !from || from->type!=cJSON_Object) return cJSON_Duplicate(to,1);
	cJSONUtils_SortObject(from);
	cJSONUtils_SortObject(to);
	from=from->child;to=to->child;
	patch=cJSON_CreateObject();
	while (from || to)
	{
		int compare=from?(to?strcmp(from->string,to->string):-1):1;
		if (compare<0)
		{
			cJSON_AddItemToObject(patch,from->string,cJSON_CreateNull());
			from=from->next;
		}
		else if (compare>0)
		{
			cJSON_AddItemToObject(patch,to->string,cJSON_Duplicate(to,1));
			to=to->next;
		}
		else
		{
			if (cJSONUtils_Compare(from,to)) cJSON_AddItemToObject(patch,to->string,cJSONUtils_GenerateMergePatch(from,to));
			from=from->next;to=to->next;
		}
	}
	if (!patch->child) {cJSON_Delete(patch);return 0;}
	return patch;
M
Max Bruckner 已提交
457
}