react-sync-state
Version:
A client side implementation for synchronizing states with server
245 lines (209 loc) • 8.09 kB
JavaScript
'use strict';
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
Object.defineProperty(exports, "__esModule", {
value: true
});
var _Record = require('../_Record');
var _Record2 = _interopRequireDefault(_Record);
var _RecordSet = require('../_RecordSet');
var _RecordSet2 = _interopRequireDefault(_RecordSet);
var _State = require('../State');
var _State2 = _interopRequireDefault(_State);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* The state initialization method, that takes in the state of the system in JSON in the following format
* <code>
* {
* "oracle":[
* {
* "type":"Unit",
* "structure":[
* {
* "name":"id",
* "type":"Long"
* },
* {
* "name":"name",
* "type":"String"
* }
* ],
* "data": {
* "1":[1,Celcius]
* }
* },
* {
* "type":"Station",
* "structure":[
* {
* "name":"id",
* "type":"Long"
* },
* {
* "name":"name",
* "type":"String"
* },
* {
* "name":"parameters",
* "type":"Parameter[]"
* }
* ],
* "data":{
* "1":[1,Kathmandu,[1]]
* }
* }
* ]
* },
* "state":[
* {
* "name":"stations",
* "type":"Station",
* "items":[1]
* }
* ],
* </code>
*
* and creates a new state based on that
*
*/
var JavaSyncStateLoader = (function () {
function JavaSyncStateLoader() {
_classCallCheck(this, JavaSyncStateLoader);
// Initialize
this.shadows = [];
}
/**
* The method to create a state from the given data
*
* @param {JSON} data The JSON data in the above format
* @return {State} The newly created State for use in the Application
*/
_createClass(JavaSyncStateLoader, [{
key: 'load',
value: function load(data) {
var state = new _State2.default();
// Lock the state, we don't want any event to be thrown during the loading
// stage
state.lock();
// First define all the data types
for (var i in data.oracle) {
var type = data.oracle[i];
// for each different record type, register two different data types
// one for the Record.Type itself and another for RecordSet.Type
var recordType = new _Record2.default.Type(type.type);
state.registerDataType(type.type, recordType);
state.registerDataType(type.type + "[]", new _RecordSet2.default.Type(recordType));
}
// Next convert all the data to a Record type
for (var j in data.oracle) {
var type = data.oracle[j];
var recordType = state.getDataType(type.type);
// first set the structure for the recordType that was created on the loop before
recordType._structure = this.createStructure(state, type.structure);
for (var id in type.data) {
var record = this.createRecord(state, recordType, type.structure, type.data[id]);
recordType._own(id, record);
}
}
// Reconile the shadow instances
while (this.shadows.length) {
var shadow = this.shadows.pop();
// A shadow consists of 4 indices which are
// 0 - The source which needs to be updated which is either Record or RecordSet
// 1 - The reference on the source which is either fieldname (for Record) or index (for RecordSet)
// 2 - The record type
// 3 - The record id
shadow[0].update([shadow[1]], shadow[2].get([shadow[3]]));
}
// Load all the states
for (var s in data.state) {
var item = data.state[s];
var list = new _RecordSet2.default(state);
state._seeds[item.name] = list;
var recordType = state.getDataType(item.type);
for (var i in item.items) {
list.add(recordType.get(item.items[i]));
}
}
// release the lock without dispatching events
state.reset();
return state;
}
/**
* The structure defined in the source data is an array of a key value
* pair. Convert that key value pair array into a object.
*/
}, {
key: 'createStructure',
value: function createStructure(state, array) {
var structure = {};
for (var i in array) {
var typeName = array[i].type;
var type = state.getDataType(typeName);
if (type == null) {
console.error("Unknown data type " + typeName);
}
structure[array[i].name] = state.getDataType(array[i].type);
}
return structure;
}
/**
* Convert the given record array into a Record object. The record object
* requires, the structure type as registered in the state, that is why
* the typeName is provided, whereas to decode the data, the structureArray
* is needed
*/
}, {
key: 'createRecord',
value: function createRecord(state, recordType, structureArray, recordArray) {
var record = new _Record2.default(state, recordType);
for (var i in recordArray) {
var field = structureArray[i].name;
var fieldType = recordType.getFieldType(field);
var value = recordArray[i];
// The value has to be interpreted depending on the field Type
if (fieldType instanceof _Record2.default.Type) {
// We expect a number that points to another record here
if (fieldType.has(value)) {
record[field] = fieldType.get(value);
} else {
// The referenced record has not been loaded here, so we will just
// put up a shadow here, which need to be reconciled later
record[field] = this.createShadow(record, field, fieldType, value);
}
} else if (fieldType instanceof _RecordSet2.default.Type) {
var refType = fieldType.getRecordType();
var set = new _RecordSet2.default(state);
// We expect a list of numbers
for (var idx in value) {
var v = value[idx];
if (refType.has(v)) {
set.add(refType.get(v));
} else {
set.add(this.createShadow(set, idx, refType, v));
}
}
record[field] = set;
} else {
// A primitive value is directly adapted
console.log(fieldType);
record[field] = fieldType.adapt(value);
}
}
return record;
}
/**
* Create a shadow instance for a record which has not yet been initialized
* but is tried to be referenced. The shadow keeps information of all these
* yet to be created record, and at the end of the initialization process
* replaces these shadows with a Record instance during reconcilation
*/
}, {
key: 'createShadow',
value: function createShadow(owner, ownerRef, recordType, recordId) {
this.shadows.push([owner, ownerRef, recordType, recordId]);
}
}]);
return JavaSyncStateLoader;
})();
exports.default = JavaSyncStateLoader;