cJSON_Utils.c 14.2 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
char *cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target)
102
{
103 104 105
    int type = object->type;
    int c = 0;
    cJSON *obj = 0;
106

107 108 109 110 111
    if (object == target)
    {
        /* found */
        return strdup("");
    }
112

113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
    /* recursively search all children of the object */
    for (obj = object->child; obj; obj = obj->next, c++)
    {
        char *found = cJSONUtils_FindPointerFromObjectTo(obj, target);
        if (found)
        {
            if (type == cJSON_Array)
            {
                /* reserve enough memory for a 64 bit integer + '/' and '\0' */
                char *ret = (char*)malloc(strlen(found) + 23);
                sprintf(ret, "/%d%s", c, found); /* /<array_index><path> */
                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;
            }

            /* reached leaf of the tree, found nothing */
            free(found);
            return 0;
        }
    }

    /* not found */
    return 0;
147 148
}

149
cJSON *cJSONUtils_GetPointer(cJSON *object, const char *pointer)
150
{
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
    /* follow path of the pointer */
    while ((*pointer++ == '/') && object)
    {
        if (object->type == cJSON_Array)
        {
            int which = 0;
            /* parse array index */
            while ((*pointer >= '0') && (*pointer <= '9'))
            {
                which = (10 * which) + (*pointer++ - '0');
            }
            if (*pointer && (*pointer != '/'))
            {
                /* not end of string or new path token */
                return 0;
            }
            object = cJSON_GetArrayItem(object, which);
        }
        else if (object->type == cJSON_Object)
        {
            object = object->child;
            /* GetObjectItem. */
            while (object && cJSONUtils_Pstrcasecmp(object->string, pointer))
            {
                object = object->next;
            }
            /* skip to the next path token or end of string */
            while (*pointer && (*pointer != '/'))
            {
                pointer++;
            }
        }
        else
        {
            return 0;
        }
    }

    return object;
190 191
}

192
/* JSON Patch implementation. */
193 194 195
static void cJSONUtils_InplaceDecodePointerString(char *string)
{
	char *s2=string;
196
	for (;*string;s2++,string++) *s2=(*string!='~')?(*string):((*(++string)=='0')?'~':'/');
197 198 199 200 201
	*s2=0;
}

static cJSON *cJSONUtils_PatchDetach(cJSON *object,const char *path)
{
202
	char *parentptr=0,*childptr=0;cJSON *parent=0,*ret=0;
203 204 205 206 207

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

208
	if (!parent) ret=0;	/* Couldn't find object to remove child from. */
209 210 211 212 213 214
	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;
}

215 216
static int cJSONUtils_Compare(cJSON *a,cJSON *b)
{
217
	if (a->type!=b->type)	return -1;	/* mismatched type. */
218 219
	switch (a->type)
	{
220 221
	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. */
222
	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;}
223
						return (a || b)?-4:0;	/* array size mismatch. */
224
	case cJSON_Object:
225 226 227 228
						cJSONUtils_SortObject(a);
						cJSONUtils_SortObject(b);
						a=a->child,b=b->child;
						while (a && b)
229
						{
230 231 232 233
							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;
234
						}
235 236
						return (a || b)?-5:0;	/* object length mismatch */

237 238 239 240 241
	default:			break;
	}
	return 0;
}

242 243
static int cJSONUtils_ApplyPatch(cJSON *object,cJSON *patch)
{
244 245
	cJSON *op=0,*path=0,*value=0,*parent=0;int opcode=0;char *parentptr=0,*childptr=0;

246 247
	op=cJSON_GetObjectItem(patch,"op");
	path=cJSON_GetObjectItem(patch,"path");
248
	if (!op || !path) return 2;	/* malformed patch. */
249 250 251 252 253 254

	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;
255
	else if (!strcmp(op->valuestring,"test"))	return cJSONUtils_Compare(cJSONUtils_GetPointer(object,path->valuestring),cJSON_GetObjectItem(patch,"value"));
256
	else return 3; /* unknown opcode. */
257

258
	if (opcode==1 || opcode==2)	/* Remove/Replace */
259
	{
260 261
		cJSON_Delete(cJSONUtils_PatchDetach(object,path->valuestring));	/* Get rid of old. */
		if (opcode==1) return 0;	/* For Remove, this is job done. */
262 263
	}

264
	if (opcode==3 || opcode==4)	/* Copy/Move uses "from". */
265
	{
266
		cJSON *from=cJSON_GetObjectItem(patch,"from");	if (!from) return 4; /* missing "from" for copy/move. */
267 268 269

		if (opcode==3) value=cJSONUtils_PatchDetach(object,from->valuestring);
		if (opcode==4) value=cJSONUtils_GetPointer(object,from->valuestring);
270
		if (!value) return 5; /* missing "from" for copy/move. */
271
		if (opcode==4) value=cJSON_Duplicate(value,1);
272
		if (!value) return 6; /* out of memory for copy/move. */
273
	}
274
	else	/* Add/Replace uses "value". */
275 276
	{
		value=cJSON_GetObjectItem(patch,"value");
277
		if (!value) return 7; /* missing "value" for add/replace. */
278
		value=cJSON_Duplicate(value,1);
279
		if (!value) return 8; /* out of memory for add/replace. */
280 281
	}
		
282
	/* Now, just add "value" to "path". */
283 284 285 286 287

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

288
	/* add, remove, replace, move, copy, test. */
M
Max Bruckner 已提交
289
	if (!parent) {free(parentptr); cJSON_Delete(value); return 9;}	/* Couldn't find object to add to. */
290 291 292 293 294 295 296 297 298 299
	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 已提交
300 301 302 303
	else
	{
		cJSON_Delete(value);
	}
304 305 306 307 308 309 310 311
	free(parentptr);
	return 0;
}


int cJSONUtils_ApplyPatches(cJSON *object,cJSON *patches)
{
	int err;
M
Max Bruckner 已提交
312
	if (patches->type!=cJSON_Array) return 1;	/* malformed patches. */
313 314 315 316 317 318 319 320
	if (patches) patches=patches->child;
	while (patches)
	{
		if ((err=cJSONUtils_ApplyPatch(object,patches))) return err;
		patches=patches->next;
	}
	return 0;
}
321

322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
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:
	{
358
		int c;char *newpath=(char*)malloc(strlen(path)+23);	/* Allow space for 64bit int. */
359 360 361 362 363 364 365 366 367 368
		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:
369
	{
370 371 372 373 374 375
		cJSON *a,*b;
		cJSONUtils_SortObject(from);
		cJSONUtils_SortObject(to);
		
		a=from->child,b=to->child;
		while (a || b)
376
		{
377 378
			int diff=(!a)?1:(!b)?-1:cJSONUtils_strcasecmp(a->string,b->string);
			if (!diff)
379 380 381
			{
				char *newpath=(char*)malloc(strlen(path)+cJSONUtils_PointerEncodedstrlen(a->string)+2);
				cJSONUtils_PointerEncodedstrcpy(newpath+sprintf(newpath,"%s/",path),a->string);
382
				cJSONUtils_CompareToPatch(patches,newpath,a,b);
383
				free(newpath);
384 385
				a=a->next;
				b=b->next;
386
			}
387 388
			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;}
389 390
		}
		return;
391
	}
392 393 394 395 396 397 398 399 400 401 402 403 404

	default:			break;
	}
}


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

405 406 407 408 409 410

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

	if (!list || !list->next) return list;	/* One entry is sorted already. */
411 412 413
	
	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 已提交
414
	ptr=list;
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444

	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);}
445

446 447
cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch)
{
448 449
	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();}
450

451 452 453 454 455 456 457 458 459 460 461 462 463
	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;
}
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494

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 已提交
495
}