jaydata-core
Version:
Cross-platform HTML5 data-management, JavaScript Language Query (JSLQ) support for OData, SQLite, WebSQL, IndexedDB, YQL and Facebook (packaged for Node.JS)
395 lines (377 loc) • 22.1 kB
JavaScript
$data.Class.define('$data.ModelBinder', null, null, {
constructor: function (context) {
this.context = context;
this.providerName = null;
if (this.context.storageProvider && typeof this.context.storageProvider.getType === 'function') {
this.references = !(this.context.storageProvider.providerConfiguration.modelBinderOptimization || false);
for (var i in $data.RegisteredStorageProviders) {
if ($data.RegisteredStorageProviders[i] === this.context.storageProvider.getType()) {
this.providerName = i;
}
}
}
},
_deepExtend: function (o, r) {
if (o === null || o === undefined) {
return r;
}
for (var i in r) {
if (o.hasOwnProperty(i)) {
if (typeof r[i] === 'object') {
if (Array.isArray(r[i])) {
for (var j = 0; j < r[i].length; j++) {
if (o[i].indexOf(r[i][j]) < 0) {
o[i].push(r[i][j]);
}
}
} else this._deepExtend(o[i], r[i]);
}
} else {
o[i] = r[i];
}
}
return this._finalize(o);
},
_finalize: function(o){
if (o instanceof $data.Entity) {
o.changedProperties = undefined;
o.storeToken = this.context.storeToken;
}
return o;
},
_buildSelector: function (meta, context) {
if (meta.$selector) {
if (!(Array.isArray(meta.$selector))) {
meta.$selector = [meta.$selector];
}
for (var i = 0; i < meta.$selector.length; i++) {
var selector = meta.$selector[i].replace('json:', '');
context.src += 'if(';
var path = selector.split('.');
for (var j = 0; j < path.length; j++) {
context.src += 'di.' + path.slice(0, j + 1).join('.') + (j < path.length - 1 ? ' && ' : ' !== undefined && typeof di.' + selector + ' === "object"');
}
context.src += '){di = di.' + selector + ';}' + (i < meta.$selector.length - 1 ? 'else ' : '');
}
context.src += 'if (di === null){';
if (context.iter) context.src += context.iter + ' = null;';
context.src += 'return null;';
context.src += '}';
}
},
_buildKey: function (name, type, keys, context, data) {
if (keys) {
var type = Container.resolveType(type);
var typeIndex = Container.getIndex(type);
type = type.fullName || type.name;
context.src += 'var ' + name + 'Fn = function(di){';
if (!(Array.isArray(keys)) || keys.length == 1) {
if (typeof keys !== 'string') keys = keys[0];
context.src += 'if (typeof di.' + keys + ' === "undefined") return undefined;';
context.src += 'if (di.' + keys + ' === null) return null;';
context.src += 'var key = ("' + type + '_' + typeIndex + '_' + keys + '#" + di.' + keys + ');';
} else {
context.src += 'var key = "";';
for (var i = 0; i < keys.length; i++) {
var id = typeof keys[i] !== 'object' ? keys[i] : keys[i].$source;
context.src += 'if (typeof di.' + id + ' === "undefined") return undefined;';
context.src += 'if (di.' + id + ' === null) return null;';
context.src += 'key += ("' + type + '_' + typeIndex + '_' + id + '#" + di.' + id + ');';
}
}
context.src += 'return key;};';
}
context.src += 'var ' + name + ' = ' + (keys ? name + 'Fn(' + (data || 'di') + ')' : 'undefined') + ';';
},
build: function (meta, context) {
if (meta.$selector) {
if (!(Array.isArray(meta.$selector))) meta.$selector = [meta.$selector];
for (var i = 0; i < meta.$selector.length; i++) {
meta.$selector[i] = meta.$selector[i].replace('json:', '');
}
}
if (meta.$value) {
if (typeof meta.$value === 'function') {
context.src += 'var di = di || data;';
context.src += 'var fn = function(){ return meta' + (context.meta.length ? '.' + context.meta.join('.') : '') + '.$value.call(self, meta' + (context.meta.length ? '.' + context.meta.join('.') : '') + ', di); };';
if (meta.$type) {
var type = Container.resolveName(Container.resolveType(meta.$type));
var typeIndex = Container.getIndex(Container.resolveType(meta.$type));
var converter = this.context.storageProvider.fieldConverter.fromDb[type];
if (converter) {
context.item = 'self.context.storageProvider.fieldConverter.fromDb["' + type + '"](fn())';
} else {
context.item = 'new (Container.resolveByIndex(' + typeIndex + '))(fn())';
}
} else context.item = 'fn()';
} else if (meta.$type) {
var type = Container.resolveName(Container.resolveType(meta.$type));
var typeIndex = Container.getIndex(Container.resolveType(meta.$type));
var converter = this.context.storageProvider.fieldConverter.fromDb[type];
if (converter) {
context.item = 'self.context.storageProvider.fieldConverter.fromDb["' + type + '"](' + meta.$value + ')';
} else {
context.item = 'new (Container.resolveByIndex(' + typeIndex + '))(' + meta.$value + ')';
}
} else context.item = meta.$value;
} else if (meta.$source) {
var type = Container.resolveName(Container.resolveType(meta.$type));
var typeIndex = Container.getIndex(Container.resolveType(meta.$type));
var converter = this.context.storageProvider.fieldConverter.fromDb[type];
var item = '_' + type.replace(/\./gi, '_') + '_';
if (!context.forEach) context.src += 'var di = data;';
context.item = item;
this._buildSelector(meta, context);
if (converter) {
context.src += 'var ' + item + ' = self.context.storageProvider.fieldConverter.fromDb["' + type + '"](di.' + meta.$source + ');';
} else {
context.src += 'var ' + item + ' = new (Container.resolveByIndex(' + typeIndex + '))(di.' + meta.$source + ');';
}
} else if (meta.$item) {
context.meta.push('$item');
var iter = (context.item && context.current ? context.item + '.' + context.current : (context.item ? context.item : 'result'));
context.iter = iter;
if (iter.indexOf('.') < 0) context.src += 'var ' + iter + ';';
context.src += 'var fn = function(di){';
if (meta.$selector) {
context.src += 'if (typeof di !== "undefined" && !(Array.isArray(di))){';
this._buildSelector(meta, context);
context.src += '}';
}
if (this.references && meta.$keys) this._buildKey('forKey', meta.$type, meta.$keys, context);
//else if (this.references && meta.$item && meta.$item.$keys) this._buildKey('forKey', meta.$type, meta.$item.$keys, context);
//else context.src += 'var forKey = typeof itemKey !== "undefined" ? itemKey : undefined;';
/*context.src += 'if (typeof forKey !== "undefined" && forKey){';
context.src += 'if (cache[forKey]){';
context.src += iter + ' = cache[forKey];';
context.src += '}else{';
context.src += iter + ' = [];';
context.src += 'cache[forKey] = ' + iter + ';';
context.src += '}';
context.src += '}else{';
context.src += iter + ' = [];';
context.src += '}';*/
context.src += iter + ' = typeof ' + iter + ' == "undefined" ? [] : ' + iter + ';';
//context.src += iter + ' = [];';
if (this.references && meta.$item.$keys) {
var keycacheName = 'keycache_' + iter.replace(/\./gi, '_');
context.src += 'var ' + keycacheName + ';';
context.src += 'var kci = keycacheIter.indexOf(' + iter + ');';
context.src += 'if (kci < 0){';
context.src += keycacheName + ' = [];';
context.src += 'keycache.push(' + keycacheName + ');';
context.src += 'keycacheIter.push(' + iter + ');';
context.src += '}else{';
context.src += keycacheName + ' = keycache[kci];';
context.src += '}';
//context.src += 'var ' + keycacheName + ' = ' + (meta.$item.$keys ? '[]' : 'null') + ';';
}
context.iter = undefined;
context.forEach = true;
var itemForKey = 'itemForKey_' + iter.replace(/\./gi, '_');
context.src += 'var forEachFn = function(di, i){';
context.src += 'var diBackup = di;';
if (this.providerName == "sqLite" && this.references && meta.$item.$keys) this._buildKey(itemForKey, meta.$type, meta.$item.$keys, context);
var item = context.item || 'iter';
context.item = item;
if (!meta.$item.$source) {
this._buildSelector(meta.$item, context);
}
this.build(meta.$item, context);
if (this.references && meta.$keys) {
context.src += 'if (forKey){';
context.src += 'if (cache[forKey]){';
context.src += iter + ' = cache[forKey];';
context.src += 'if (' + iter + '.indexOf(' + (context.item || item) + ') < 0){';
context.src += iter + '.push(' + (context.item || item) + ');';
context.src += '}}else{';
context.src += 'cache[forKey] = ' + iter + ';';
context.src += iter + '.push(' + (context.item || item) + ');';
context.src += '}}else{';
if (this.references && meta.$item.$keys) this._buildKey('cacheKey', meta.$type, meta.$item.$keys, context, 'diBackup');
context.src += 'if (typeof cacheKey != "undefined" && cacheKey !== null){';
context.src += 'if (keycache_' + iter.replace(/\./gi, '_') + ' && cacheKey){';
context.src += 'if (keycache_' + iter.replace(/\./gi, '_') + '.indexOf(cacheKey) < 0){';
context.src += iter + '.push(' + (context.item || item) + ');';
context.src += 'keycache_' + iter.replace(/\./gi, '_') + '.push(cacheKey);';
context.src += '}';
context.src += '}else{';
context.src += iter + '.push(' + (context.item || item) + ');';
context.src += '}';
context.src += '}';
context.src += '}';
} else {
if (this.references && meta.$item.$keys) {
context.src += 'if (typeof ' + itemForKey + ' !== "undefined" && ' + itemForKey + ' !== null){';
context.src += 'if (typeof keycache_' + iter.replace(/\./gi, '_') + ' !== "undefined" && ' + itemForKey + '){';
context.src += 'if (keycache_' + iter.replace(/\./gi, '_') + '.indexOf(' + itemForKey + ') < 0){';
context.src += iter + '.push(' + (context.item || item) + ');';
context.src += 'keycache_' + iter.replace(/\./gi, '_') + '.push(' + itemForKey + ');'
context.src += '}}else{';
context.src += iter + '.push(' + (context.item || item) + ');';
context.src += '}}else{';
context.src += iter + '.push(' + (context.item || item) + ');';
context.src += '}';
/*context.src += 'if (typeof itemKey !== "undefined" && itemKey !== null){';
context.src += 'if (typeof keycache_' + iter.replace(/\./gi, '_') + ' !== "undefined" && itemKey){';
context.src += 'if (keycache_' + iter.replace(/\./gi, '_') + '.indexOf(itemKey) < 0){';
context.src += iter + '.push(' + (context.item || item) + ');';
context.src += 'keycache_' + iter.replace(/\./gi, '_') + '.push(itemKey);'
context.src += '}}else{';
context.src += iter + '.push(' + (context.item || item) + ');';
context.src += '}}else{';
context.src += iter + '.push(' + (context.item || item) + ');';
context.src += '}';*/
} else {
context.src += iter + '.push(' + (context.item || item) + ');';
}
}
context.src += '};';
context.src += 'if (Array.isArray(di)) di.forEach(forEachFn);';
context.src += 'else forEachFn(di, 0);';
context.forEach = false;
context.item = null;
context.src += '};fn(typeof di === "undefined" ? data : di);'
context.meta.pop();
} else if (meta.$type) {
if (!context.forEach) {
context.src += 'if (typeof di === "undefined"){';
context.src += 'var di = data;';
this._buildSelector(meta, context);
context.src += '}';
}
var resolvedType = Container.resolveType(meta.$type);
var type = Container.resolveName(resolvedType);
var typeIndex = Container.getIndex(resolvedType);
var isEntityType = resolvedType.isAssignableTo && resolvedType.isAssignableTo($data.Entity);
var item = '_' + type.replace(/\./gi, '_') + '_';
if (context.item == item) item += 'new_';
context.item = item;
var isPrimitive = false;
if (!meta.$source && !meta.$value && resolvedType !== $data.Array && resolvedType !== $data.Object && !resolvedType.isAssignableTo)
isPrimitive = true;
if (resolvedType === $data.Object || resolvedType === $data.Array) {
var keys = Object.keys(meta);
if (keys.length == 1 || (keys.length == 2 && meta.$selector)) isPrimitive = true;
}
if (isPrimitive) {
var converter = this.context.storageProvider.fieldConverter.fromDb[type];
if (converter) {
context.src += 'var ' + item + ' = di != undefined ? self.context.storageProvider.fieldConverter.fromDb["' + type + '"](di) : di;';
} else {
context.src += 'var ' + item + ' = di;';
}
} else {
if (this.references && meta.$keys) {
this._buildKey('itemKey', meta.$type, meta.$keys, context);
context.src += 'if (itemKey === null) return null;';
context.src += 'var ' + item + ';';
context.src += 'if (itemKey && cache[itemKey]){';
context.src += item + ' = cache[itemKey];';
context.src += '}else{';
if (isEntityType) {
context.src += item + ' = new (Container.resolveByIndex(' + typeIndex + '))(undefined, { setDefaultValues: false });';
} else {
context.src += item + ' = new (Container.resolveByIndex(' + typeIndex + '))();';
}
context.src += 'if (itemKey){';
context.src += 'cache[itemKey] = ' + item + ';';
context.src += '}';
context.src += '}';
} else {
if (isEntityType) {
context.src += 'var ' + item + ' = new (Container.resolveByIndex(' + typeIndex + '))(undefined, { setDefaultValues: false });';
} else {
context.src += 'var ' + item + ' = new (Container.resolveByIndex(' + typeIndex + '))();';
}
}
}
for (var i in meta) {
if (i.indexOf('$') < 0) {
context.current = i;
if (!meta[i].$item) {
if (meta[i].$value) {
context.meta.push(i);
var item = context.item;
this.build(meta[i], context);
context.src += item + '.' + i + ' = ' + context.item + ';';
context.item = item;
context.meta.pop();
} else if (meta[i].$source) {
context.src += 'var fn = function(di){';
this._buildSelector(meta[i], context);
if (meta[i].$type) {
var type = Container.resolveName(Container.resolveType(meta[i].$type));
var typeIndex = Container.getIndex(Container.resolveType(meta[i].$type));
var converter = this.context.storageProvider.fieldConverter.fromDb[type];
if (converter) {
context.src += 'return self.context.storageProvider.fieldConverter.fromDb["' + type + '"](di.' + meta[i].$source + ');';
} else {
context.src += 'return new (Container.resolveByIndex(' + typeIndex + '))(di.' + meta[i].$source + ');';
}
} else {
context.src += item + '.' + i + ' = di.' + meta[i].$source + ';';
}
context.src += '};';
if (meta[i].$type) context.src += item + '.' + i + ' = fn(di);';
else context.src += 'fn(di);';
} else if (meta[i].$type) {
context.meta.push(i);
context.src += 'var fn = function(di){';
this._buildSelector(meta[i], context);
this.build(meta[i], context);
context.src += 'return ' + context.item + ';};';
if (meta[i].$type === $data.Object) context.src += item + '.' + i + ' = self._deepExtend(' + item + '.' + i + ', fn(di));';
else context.src += item + '.' + i + ' = fn(di);';
context.item = item;
context.meta.pop();
} else if (meta.$type) {
var memDef = Container.resolveType(meta.$type).memberDefinitions.getMember(i);
var type = Container.resolveName(memDef.type);
var entityType = Container.resolveType(meta.$type);
var entityTypeIndex = Container.getIndex(meta.$type);
var converter = this.context.storageProvider.fieldConverter.fromDb[type];
if (this.providerName && memDef && memDef.converter && memDef.converter[this.providerName] && typeof memDef.converter[this.providerName].fromDb == 'function') {
context.src += item + '.' + i + ' = Container.resolveByIndex("' + entityTypeIndex + '").memberDefinitions.getMember("' + i + '").converter.' + this.providerName + '.fromDb(di.' + meta[i] + ', Container.resolveByIndex("' + entityTypeIndex + '").memberDefinitions.getMember("' + i + '"), self.context, Container.resolveByIndex("' + entityTypeIndex + '"));';
} else if (converter) {
context.src += item + '.' + i + ' = self.context.storageProvider.fieldConverter.fromDb["' + type + '"](di.' + meta[i] + ');';
} else {
//var type = Container.resolveName(Container.resolveType(type.memberDefinitions.getMember(i).type));
var typeIndex = Container.getIndex(Container.resolveType(type.memberDefinitions.getMember(i).type));
context.src += item + '.' + i + ' = new (Container.resolveByIndex(' + typeIndex + '))(di.' + meta[i] + ');';
}
}
} else {
context.meta.push(i);
this.build(meta[i], context);
context.item = item;
context.meta.pop();
}
}
}
context.src += item + ' = self._finalize(' + item + ');';
}
},
call: function (data, meta) {
if (!Object.getOwnPropertyNames(meta).length) {
return data;
}
var context = {
src: '',
meta: []
};
context.src += 'var self = this;';
context.src += 'var result;';
context.src += 'var cache = {};';
context.src += 'var keycache = [];';
context.src += 'var keycacheIter = [];';
this.build(meta, context);
if (context.item) context.src += 'if (typeof result === "undefined") result = ' + context.item + ';';
context.src += 'return result;';
/*var beautify = require('beautifyjs');
console.log(beautify.js_beautify(context.src));*/
var fn = new Function('meta', 'data', context.src).bind(this);
var ret = fn(meta, data);
return ret;
}
});