UNPKG

json-diff-rfc6902

Version:

This framework is to compare two JSON data and generate the Patch

236 lines (201 loc) 6.2 kB
var unchangedArea = require('./unchangedArea.js'); var patchArea = require('./patchArea.js'); var hashObject = require('./hashObject.js'); var applyPatches = require('./applyPatches'); exports.transformArray = transformArray; function transformIndex(element, m, array) { var finalIndex; switch(element.op) { case 'add': case 'replace': case 'copy': // When add, replace and copy, add directly return element.index; case 'remove': finalIndex = element.index; break; case 'move': finalIndex = element.from; break; } for (var i = 0; i < m; i++) { switch (array[i].op) { case 'remove': if(finalIndex > array[i].index) { // when equal, don't -- finalIndex --; } break; case 'add': case 'copy': if(finalIndex >= array[i].index) { // when equal, do ++ finalIndex ++; } break; case 'replace': // When equal, don't change break; case 'move': if (array[i].from !== array[i].index) { var min = Math.min(array[i].from, array[i].index); var max = Math.max(array[i].from, array[i].index); if (finalIndex >= min && finalIndex <= max) { if (array[i].from > array[i].index) { finalIndex ++; } else { finalIndex --; } } } break; } } return finalIndex; } function operationValue (op) { switch (op) { case "move" : return 0; case "remove" : return 1; case "add" : return 2; case "replace": return 3; case "copy" : return 4; } } function compare(a, b) { if (a.index === b.index) { // Order: move < remove < add var a_value = operationValue(a.op); var b_value = operationValue(b.op); if (a_value > b_value) { return 1; } else { return -1; } } else { return a.index - b.index; } } function findCopyInArray(element, m, array, arrUnchanged) { var copyIndex = -1; for (var i = 0; i < m; i++) { switch (element.op) { case 'remove': case 'copy': break; default: // Move Replace Add if (element.hash === array[i].hash) { return array[i].index; } break; } } //Find value in arrUnchanged for (var j= 0; j< arrUnchanged.length; j++) { if (element.hash === arrUnchanged[j].hash) { return arrUnchanged[j].index; } } return copyIndex; } function transformArray(oldJson, newJson, unchanged, patches, patchHashes, path) { //When is the Array, stop to find leaf node // (hash, value, index) var x = hashObject.mapArray(hashObject.hash, oldJson); var y = hashObject.mapArray(hashObject.hash, newJson); // Reserve the origin index // COPY ARRAY var x_sorted = x.slice(); var y_sorted = y.slice(); x_sorted.sort(function(a, b) {return a.hash - b.hash;}); y_sorted.sort(function(a, b) {return a.hash - b.hash;}); //Diff var arrPatch = [], arrUnchanged = []; var i= 0, j = 0; while (i < x_sorted.length) { while( j < y_sorted.length) { if(x_sorted[i] !== void 0) { if (x_sorted[i].hash > y_sorted[j].hash) { arrPatch.push({op: "add", value: y_sorted[j].value, index: y_sorted[j].index, hash: y_sorted[j].hash }); j++; } else if (x_sorted[i].hash === y_sorted[j].hash) { // Unchanged push unchanged.push( path + '/' + y_sorted[j].index + "=" + JSON.stringify(x_sorted[i].hash)); arrPatch.push({op: "move", value: y_sorted[j].value, from: x_sorted[i].index , index: y_sorted[j].index, hash: y_sorted[j].hash }); i++; j++; } else { arrPatch.push({op: "remove", index: x_sorted[i].index}); i++; } } else { arrPatch.push({op: "add", value: y_sorted[j].value, index: y_sorted[j].index, hash: y_sorted[j].hash }); j++; } } if (i < x_sorted.length) { // Remove the rest elements of the x_sorted arrPatch.push({op: "remove", index: x_sorted[i].index }); i++; } } //Get the patch to make all the elements are the same, but index is random arrPatch = arrPatch.sort(compare); // console.log(arrPatch); var m = 0; while(arrPatch[m] !== void 0) { // f_index = transformIndex(arrPatch[m], arrPatch); switch(arrPatch[m].op) { case 'add': arrPatch[m].index = transformIndex(arrPatch[m], m, arrPatch); // replace if (arrPatch[m-1] !== void 0) { if (arrPatch[m-1].op === 'remove' && arrPatch[m-1].index === arrPatch[m].index) { arrPatch[m].op = 'replace'; arrPatch.splice(m-1,1); continue; } } // COPY var copyIndex = findCopyInArray(arrPatch[m], m, arrPatch, arrUnchanged); if (copyIndex !== -1) { arrPatch[m].op = 'copy'; arrPatch[m].from = copyIndex; if (arrPatch[m].index === arrPatch[m].from) { arrPatch.splice(m, 1); continue; } } break; case 'remove': arrPatch[m].index = transformIndex(arrPatch[m], m, arrPatch); break; case 'move': arrPatch[m].from = transformIndex(arrPatch[m], m, arrPatch); if (arrPatch[m].index === arrPatch[m].from) { arrUnchanged.push(arrPatch[m]); arrPatch.splice(m, 1); continue; } break; } m++; } // console.log("=========================="); // console.log(arrPatch); // // console.log("========Final==============="); arrPatch = arrPatch.map(function(obj) { obj.path = path + '/' + obj.index; delete obj.hash; delete obj.index; if (obj.op === 'move' || obj.op === 'copy') { obj.from = path + '/' + obj.from; delete obj.value; } return obj; }); // console.log(arrPatch); return arrPatch; }