breeze-client
Version:
Breeze data management for JavaScript clients
277 lines (273 loc) • 10.7 kB
JavaScript
import * as breeze from 'breeze-client';
let core = breeze.core;
class ModelLibraryBackingStoreAdapter {
name;
constructor() {
this.name = "backingStore";
}
static register(config) {
config = config || breeze.config;
config.registerAdapter("modelLibrary", ModelLibraryBackingStoreAdapter);
return config.initializeAdapterInstance("modelLibrary", "backingStore", true);
}
initialize() {
}
getTrackablePropertyNames(entity) {
let names = [];
for (let p in entity) {
if (p === "entityAspect" || p === "entityType")
continue;
if (p === "_$typeName" || p === "_pendingSets" || p === "_backingStore")
continue;
let val = entity[p];
if (!core.isFunction(val)) {
names.push(p);
}
}
return names;
}
// This method is called during Metadata initialization
initializeEntityPrototype(proto) {
proto.getProperty = function (propertyName) {
return this[propertyName];
};
proto.setProperty = function (propertyName, value) {
//if (!this._backingStore.hasOwnProperty(propertyName)) {
// throw new Error("Unknown property name:" + propertyName);
//}
this[propertyName] = value;
// allow setProperty chaining.
return this;
};
movePropDefsToProto(proto);
}
// This method is called when an EntityAspect is first created - this will occur as part of the entityType.createEntity call.
// which can be called either directly or via standard query materialization
// entity is either an entity or a complexObject
startTracking(entity, proto) {
// can't touch the normal property sets within this method - access the backingStore directly instead.
let bs = movePropsToBackingStore(entity);
// assign default values to the entity
let stype = breeze.EntityAspect.isEntity(entity) ? entity.entityType : entity.complexType;
stype.getProperties().forEach(function (prop) {
let propName = prop.name;
let val = entity[propName];
if (prop instanceof breeze.DataProperty) {
if (prop.isComplexProperty) {
if (prop.isScalar) {
val = prop.dataType._createInstanceCore(entity, prop);
}
else {
val = breeze.makeComplexArray([], entity, prop);
}
}
else if (!prop.isScalar) {
val = breeze.makePrimitiveArray([], entity, prop);
}
else if (val === undefined) {
val = prop.defaultValue;
}
}
else if (prop.isNavigationProperty) {
if (val !== undefined && val !== null) {
throw new Error("Cannot assign a navigation property in an entity ctor.: " + propName);
}
if (prop.isScalar) {
// TODO: change this to nullstob later.
val = null;
}
else {
val = breeze.makeRelationArray([], entity, prop);
}
}
else {
throw new Error("unknown property: " + propName);
}
// can't touch the normal property sets within this method (IE9 Bug) - so we access the backingStore directly instead.
// otherwise we could just do
// entity[propName] = val
// after all of the interception logic had been injected.
if (prop.isSettable || prop.isNavigationProperty) {
bs[propName] = val;
}
});
}
}
breeze.config.registerAdapter("modelLibrary", ModelLibraryBackingStoreAdapter);
// private methods
// This method is called during Metadata initialization to correctly "wrap" properties.
function movePropDefsToProto(proto) {
let stype = (proto.entityType || proto.complexType);
let extra = stype._extra;
let alreadyWrapped = extra.alreadyWrappedProps || {};
stype.getProperties().forEach(function (prop) {
let propName = prop.name;
// we only want to wrap props that haven't already been wrapped
if (alreadyWrapped[propName])
return;
// If property is already defined on the prototype then wrap it in another propertyDescriptor.
// otherwise create a propDescriptor for it.
let descr;
if (propName in proto) {
descr = wrapPropDescription(proto, prop);
}
else {
descr = makePropDescription(proto, prop);
}
// descr will be null for a wrapped descr that is not configurable
if (descr != null) {
Object.defineProperty(proto, propName, descr);
}
alreadyWrapped[propName] = true;
});
extra.alreadyWrappedProps = alreadyWrapped;
}
// This method is called when an instance is first created via materialization or createEntity.
// this method cannot be called while a 'defineProperty' accessor is executing
// because of IE bug mentioned above.
function movePropsToBackingStore(instance) {
let bs = getBackingStore(instance);
let proto = Object.getPrototypeOf(instance);
let stype = (proto.entityType || proto.complexType);
stype.getProperties().forEach(function (prop) {
let propName = prop.name;
if (prop.isUnmapped) {
// insure that any unmapped properties that were added after entityType
// was first created are wrapped with a property descriptor.
if (!core.getPropertyDescriptor(proto, propName)) {
let descr = makePropDescription(proto, prop);
Object.defineProperty(proto, propName, descr);
}
}
if (!instance.hasOwnProperty(propName))
return;
// pulls off the value, removes the instance property and then rewrites it via ES5 accessor
let value = instance[propName];
delete instance[propName];
instance[propName] = value;
});
return bs;
}
function makePropDescription(proto, property) {
let propName = property.name;
let pendingStores = proto._pendingBackingStores;
if (!pendingStores) {
pendingStores = [];
proto._pendingBackingStores = pendingStores;
}
let descr = {
get: function () {
let bs = this._backingStore || getBackingStore(this);
return bs[propName];
},
set: function (value) {
// IE9 cannot touch instance._backingStore here
let bs = this._backingStore || getPendingBackingStore(this);
let accessorFn = getAccessorFn(bs, propName);
this._$interceptor(property, value, accessorFn);
},
enumerable: true,
configurable: true
};
descr.set.rawSet = function (value) {
let bs = this._backingStore || getPendingBackingStore(this);
let accessorFn = getAccessorFn(bs, propName);
accessorFn(value);
};
return descr;
}
function getAccessorFn(bs, propName) {
return function () {
if (arguments.length === 0) {
return bs[propName];
}
else {
bs[propName] = arguments[0];
return undefined;
}
};
}
function wrapPropDescription(proto, property) {
if (!proto.hasOwnProperty(property.name)) {
let nextProto = Object.getPrototypeOf(proto);
return wrapPropDescription(nextProto, property);
}
let propDescr = Object.getOwnPropertyDescriptor(proto, property.name);
if (!propDescr)
return undefined;
// if not configurable; we can't touch it - so leave.
if (!propDescr.configurable)
return undefined;
// if a data descriptor - don't change it - this is basically a static property - i.e. defined on every instance of the type with the same value.
if (propDescr.value)
return undefined;
// if a read only property descriptor - no need to change it.
if (!propDescr.set)
return undefined;
let localAccessorFn = function (entity) {
return function () {
if (!propDescr)
return undefined;
if (arguments.length === 0) {
return propDescr.get.bind(entity)();
}
else {
let set = propDescr.set;
let rawSet = set.rawSet || set;
rawSet.bind(entity)(arguments[0]);
return undefined;
}
};
};
let newDescr = {
get: function () {
if (!propDescr)
return undefined;
return propDescr.get.bind(this)();
},
set: function (value) {
this._$interceptor(property, value, localAccessorFn(this));
},
enumerable: propDescr.enumerable,
configurable: true
};
newDescr.set.rawSet = propDescr.set;
return newDescr;
}
function getBackingStore(instance) {
let proto = Object.getPrototypeOf(instance);
processPendingStores(proto);
let bs = instance._backingStore;
if (!bs) {
bs = {};
instance._backingStore = bs;
}
return bs;
}
// workaround for IE9 bug where instance properties cannot be changed when executing a property 'set' method.
function getPendingBackingStore(instance) {
let proto = Object.getPrototypeOf(instance);
let pendingStores = proto._pendingBackingStores;
let pending = core.arrayFirst(pendingStores, function (pending) {
return pending.entity === instance;
});
if (pending)
return pending.backingStore;
let bs = {};
pendingStores.push({ entity: instance, backingStore: bs });
return bs;
}
function processPendingStores(proto) {
let pendingStores = proto._pendingBackingStores;
if (pendingStores) {
pendingStores.forEach(function (pending) {
pending.entity._backingStore = pending.backingStore;
});
pendingStores.length = 0;
}
}
/**
* Generated bundle index. Do not edit.
*/
export { ModelLibraryBackingStoreAdapter };
//# sourceMappingURL=breeze-client-adapter-model-library-backing-store.mjs.map