293 lines
9.1 KiB
293 lines
9.1 KiB
|
|
/* |
|
* Licensed to the Apache Software Foundation (ASF) under one |
|
* or more contributor license agreements. See the NOTICE file |
|
* distributed with this work for additional information |
|
* regarding copyright ownership. The ASF licenses this file |
|
* to you under the Apache License, Version 2.0 (the |
|
* "License"); you may not use this file except in compliance |
|
* with the License. You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, |
|
* software distributed under the License is distributed on an |
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
|
* KIND, either express or implied. See the License for the |
|
* specific language governing permissions and limitations |
|
* under the License. |
|
*/ |
|
|
|
|
|
/** |
|
* AUTO-GENERATED FILE. DO NOT MODIFY. |
|
*/ |
|
|
|
/* |
|
* Licensed to the Apache Software Foundation (ASF) under one |
|
* or more contributor license agreements. See the NOTICE file |
|
* distributed with this work for additional information |
|
* regarding copyright ownership. The ASF licenses this file |
|
* to you under the Apache License, Version 2.0 (the |
|
* "License"); you may not use this file except in compliance |
|
* with the License. You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, |
|
* software distributed under the License is distributed on an |
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
|
* KIND, either express or implied. See the License for the |
|
* specific language governing permissions and limitations |
|
* under the License. |
|
*/ |
|
function dataIndexMapValueLength(valNumOrArrLengthMoreThan2) { |
|
return valNumOrArrLengthMoreThan2 == null ? 0 : valNumOrArrLengthMoreThan2.length || 1; |
|
} |
|
|
|
function defaultKeyGetter(item) { |
|
return item; |
|
} |
|
|
|
var DataDiffer = |
|
/** @class */ |
|
function () { |
|
/** |
|
* @param context Can be visited by this.context in callback. |
|
*/ |
|
function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context, // By default: 'oneToOne'. |
|
diffMode) { |
|
this._old = oldArr; |
|
this._new = newArr; |
|
this._oldKeyGetter = oldKeyGetter || defaultKeyGetter; |
|
this._newKeyGetter = newKeyGetter || defaultKeyGetter; // Visible in callback via `this.context`; |
|
|
|
this.context = context; |
|
this._diffModeMultiple = diffMode === 'multiple'; |
|
} |
|
/** |
|
* Callback function when add a data |
|
*/ |
|
|
|
|
|
DataDiffer.prototype.add = function (func) { |
|
this._add = func; |
|
return this; |
|
}; |
|
/** |
|
* Callback function when update a data |
|
*/ |
|
|
|
|
|
DataDiffer.prototype.update = function (func) { |
|
this._update = func; |
|
return this; |
|
}; |
|
/** |
|
* Callback function when update a data and only work in `cbMode: 'byKey'`. |
|
*/ |
|
|
|
|
|
DataDiffer.prototype.updateManyToOne = function (func) { |
|
this._updateManyToOne = func; |
|
return this; |
|
}; |
|
/** |
|
* Callback function when update a data and only work in `cbMode: 'byKey'`. |
|
*/ |
|
|
|
|
|
DataDiffer.prototype.updateOneToMany = function (func) { |
|
this._updateOneToMany = func; |
|
return this; |
|
}; |
|
/** |
|
* Callback function when update a data and only work in `cbMode: 'byKey'`. |
|
*/ |
|
|
|
|
|
DataDiffer.prototype.updateManyToMany = function (func) { |
|
this._updateManyToMany = func; |
|
return this; |
|
}; |
|
/** |
|
* Callback function when remove a data |
|
*/ |
|
|
|
|
|
DataDiffer.prototype.remove = function (func) { |
|
this._remove = func; |
|
return this; |
|
}; |
|
|
|
DataDiffer.prototype.execute = function () { |
|
this[this._diffModeMultiple ? '_executeMultiple' : '_executeOneToOne'](); |
|
}; |
|
|
|
DataDiffer.prototype._executeOneToOne = function () { |
|
var oldArr = this._old; |
|
var newArr = this._new; |
|
var newDataIndexMap = {}; |
|
var oldDataKeyArr = new Array(oldArr.length); |
|
var newDataKeyArr = new Array(newArr.length); |
|
|
|
this._initIndexMap(oldArr, null, oldDataKeyArr, '_oldKeyGetter'); |
|
|
|
this._initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter'); |
|
|
|
for (var i = 0; i < oldArr.length; i++) { |
|
var oldKey = oldDataKeyArr[i]; |
|
var newIdxMapVal = newDataIndexMap[oldKey]; |
|
var newIdxMapValLen = dataIndexMapValueLength(newIdxMapVal); // idx can never be empty array here. see 'set null' logic below. |
|
|
|
if (newIdxMapValLen > 1) { |
|
// Consider there is duplicate key (for example, use dataItem.name as key). |
|
// We should make sure every item in newArr and oldArr can be visited. |
|
var newIdx = newIdxMapVal.shift(); |
|
|
|
if (newIdxMapVal.length === 1) { |
|
newDataIndexMap[oldKey] = newIdxMapVal[0]; |
|
} |
|
|
|
this._update && this._update(newIdx, i); |
|
} else if (newIdxMapValLen === 1) { |
|
newDataIndexMap[oldKey] = null; |
|
this._update && this._update(newIdxMapVal, i); |
|
} else { |
|
this._remove && this._remove(i); |
|
} |
|
} |
|
|
|
this._performRestAdd(newDataKeyArr, newDataIndexMap); |
|
}; |
|
/** |
|
* For example, consider the case: |
|
* oldData: [o0, o1, o2, o3, o4, o5, o6, o7], |
|
* newData: [n0, n1, n2, n3, n4, n5, n6, n7, n8], |
|
* Where: |
|
* o0, o1, n0 has key 'a' (many to one) |
|
* o5, n4, n5, n6 has key 'b' (one to many) |
|
* o2, n1 has key 'c' (one to one) |
|
* n2, n3 has key 'd' (add) |
|
* o3, o4 has key 'e' (remove) |
|
* o6, o7, n7, n8 has key 'f' (many to many, treated as add and remove) |
|
* Then: |
|
* (The order of the following directives are not ensured.) |
|
* this._updateManyToOne(n0, [o0, o1]); |
|
* this._updateOneToMany([n4, n5, n6], o5); |
|
* this._update(n1, o2); |
|
* this._remove(o3); |
|
* this._remove(o4); |
|
* this._remove(o6); |
|
* this._remove(o7); |
|
* this._add(n2); |
|
* this._add(n3); |
|
* this._add(n7); |
|
* this._add(n8); |
|
*/ |
|
|
|
|
|
DataDiffer.prototype._executeMultiple = function () { |
|
var oldArr = this._old; |
|
var newArr = this._new; |
|
var oldDataIndexMap = {}; |
|
var newDataIndexMap = {}; |
|
var oldDataKeyArr = []; |
|
var newDataKeyArr = []; |
|
|
|
this._initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter'); |
|
|
|
this._initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter'); |
|
|
|
for (var i = 0; i < oldDataKeyArr.length; i++) { |
|
var oldKey = oldDataKeyArr[i]; |
|
var oldIdxMapVal = oldDataIndexMap[oldKey]; |
|
var newIdxMapVal = newDataIndexMap[oldKey]; |
|
var oldIdxMapValLen = dataIndexMapValueLength(oldIdxMapVal); |
|
var newIdxMapValLen = dataIndexMapValueLength(newIdxMapVal); |
|
|
|
if (oldIdxMapValLen > 1 && newIdxMapValLen === 1) { |
|
this._updateManyToOne && this._updateManyToOne(newIdxMapVal, oldIdxMapVal); |
|
newDataIndexMap[oldKey] = null; |
|
} else if (oldIdxMapValLen === 1 && newIdxMapValLen > 1) { |
|
this._updateOneToMany && this._updateOneToMany(newIdxMapVal, oldIdxMapVal); |
|
newDataIndexMap[oldKey] = null; |
|
} else if (oldIdxMapValLen === 1 && newIdxMapValLen === 1) { |
|
this._update && this._update(newIdxMapVal, oldIdxMapVal); |
|
newDataIndexMap[oldKey] = null; |
|
} else if (oldIdxMapValLen > 1 && newIdxMapValLen > 1) { |
|
this._updateManyToMany && this._updateManyToMany(newIdxMapVal, oldIdxMapVal); |
|
newDataIndexMap[oldKey] = null; |
|
} else if (oldIdxMapValLen > 1) { |
|
for (var i_1 = 0; i_1 < oldIdxMapValLen; i_1++) { |
|
this._remove && this._remove(oldIdxMapVal[i_1]); |
|
} |
|
} else { |
|
this._remove && this._remove(oldIdxMapVal); |
|
} |
|
} |
|
|
|
this._performRestAdd(newDataKeyArr, newDataIndexMap); |
|
}; |
|
|
|
DataDiffer.prototype._performRestAdd = function (newDataKeyArr, newDataIndexMap) { |
|
for (var i = 0; i < newDataKeyArr.length; i++) { |
|
var newKey = newDataKeyArr[i]; |
|
var newIdxMapVal = newDataIndexMap[newKey]; |
|
var idxMapValLen = dataIndexMapValueLength(newIdxMapVal); |
|
|
|
if (idxMapValLen > 1) { |
|
for (var j = 0; j < idxMapValLen; j++) { |
|
this._add && this._add(newIdxMapVal[j]); |
|
} |
|
} else if (idxMapValLen === 1) { |
|
this._add && this._add(newIdxMapVal); |
|
} // Support both `newDataKeyArr` are duplication removed or not removed. |
|
|
|
|
|
newDataIndexMap[newKey] = null; |
|
} |
|
}; |
|
|
|
DataDiffer.prototype._initIndexMap = function (arr, // Can be null. |
|
map, // In 'byKey', the output `keyArr` is duplication removed. |
|
// In 'byIndex', the output `keyArr` is not duplication removed and |
|
// its indices are accurately corresponding to `arr`. |
|
keyArr, keyGetterName) { |
|
var cbModeMultiple = this._diffModeMultiple; |
|
|
|
for (var i = 0; i < arr.length; i++) { |
|
// Add prefix to avoid conflict with Object.prototype. |
|
var key = '_ec_' + this[keyGetterName](arr[i], i); |
|
|
|
if (!cbModeMultiple) { |
|
keyArr[i] = key; |
|
} |
|
|
|
if (!map) { |
|
continue; |
|
} |
|
|
|
var idxMapVal = map[key]; |
|
var idxMapValLen = dataIndexMapValueLength(idxMapVal); |
|
|
|
if (idxMapValLen === 0) { |
|
// Simple optimize: in most cases, one index has one key, |
|
// do not need array. |
|
map[key] = i; |
|
|
|
if (cbModeMultiple) { |
|
keyArr.push(key); |
|
} |
|
} else if (idxMapValLen === 1) { |
|
map[key] = [idxMapVal, i]; |
|
} else { |
|
idxMapVal.push(i); |
|
} |
|
} |
|
}; |
|
|
|
return DataDiffer; |
|
}(); |
|
|
|
export default DataDiffer; |