thingmodel
Version:
Synchronize data and models across multiple devices
173 lines (139 loc) • 4.67 kB
text/typescript
module ThingModel {
export class Warehouse {
private _thingTypes: { [key: string]: ThingType };
private _things: { [key: string]: Thing };
private _observers: IWarehouseObserver[];
constructor() {
this._thingTypes = {};
this._things = {};
this._observers = [];
}
public RegisterType(type: ThingType, force: boolean = true, sender: string=null): void {
if (!type) {
throw new Error("The thing type information is null.");
}
if (force || !_.has(this._thingTypes, type.Name)) {
this._thingTypes[type.Name] = type;
this.NotifyThingTypeDefine(type, sender);
}
}
public RegisterThing(thing: Thing, alsoRegisterConnections: boolean = true,
alsoRegisterTypes: boolean = true, sender: string=null): void {
if (!thing) {
throw new Error("A thing should not be null if it want to be allowed in the warehouse");
}
var creation = !_.has(this._things, thing.ID);
this._things[thing.ID] = thing;
if (alsoRegisterTypes && thing.Type) {
this.RegisterType(thing.Type, false, sender);
}
if (alsoRegisterConnections) {
var alreadyVisitedObjects: { [id: string]: boolean } = {};
_.each(thing.ConnectedThings, (connectedThing) => {
this.RecursiveRegisterThing(connectedThing, alsoRegisterTypes, alreadyVisitedObjects, sender);
});
}
if (creation) {
this.NotifyThingCreation(thing, sender);
} else {
this.NotifyThingUpdate(thing, sender);
}
}
private RecursiveRegisterThing(thing: Thing, alsoRegisterTypes: boolean,
alreadyVisitedObjects: { [id: string]: boolean }, sender: string): void {
// Avoid infinite recursions
if (alreadyVisitedObjects.hasOwnProperty(thing.ID)) {
return;
}
alreadyVisitedObjects[thing.ID] = true;
this.RegisterThing(thing, false, alsoRegisterTypes, sender);
_.each(thing.ConnectedThings, (connectedThing) => {
this.RecursiveRegisterThing(connectedThing, alsoRegisterTypes, alreadyVisitedObjects, sender);
});
}
public RegisterCollection(collection: Thing[], alsoRegisterTypes: boolean=true, sender: string=null): void {
var alreadyVisitedObjects: { [id: string]: boolean } = {};
_.each(collection, (thing) => {
this.RecursiveRegisterThing(thing, alsoRegisterTypes, alreadyVisitedObjects, sender);
});
}
public RemoveCollection(collection: { [id: string]: Thing }, notifyUpdates = true, sender: string=null) {
var thingsToDisconnect: { [id: string]: Thing } = {};
_.each(_.keys(collection), (id: string) => {
var thing = collection[id];
if (notifyUpdates) {
_.each(this._things, (t: Thing) => {
if (t.IsConnectedTo(thing)) {
thingsToDisconnect[t.ID] = t;
}
});
}
this.RemoveThing(thing, false, sender);
});
if (notifyUpdates) {
_.each(_.keys(thingsToDisconnect), (id: string) => {
if (!collection.hasOwnProperty(id)) {
this.NotifyThingUpdate(thingsToDisconnect[id]);
}
});
}
}
public RemoveThing(thing: Thing, notifyUpdates = true, sender: string=null): void {
if (!thing) {
return;
}
// Remove all connections
_.each(this._things, (t: Thing) => {
if (t.IsConnectedTo(thing)) {
t.Disconnect(thing);
if (notifyUpdates) {
this.NotifyThingUpdate(t, sender);
}
}
});
if (_.has(this._things, thing.ID)) {
delete this._things[thing.ID];
this.NotifyThingDeleted(thing, sender);
}
}
public RegisterObserver(observer: IWarehouseObserver): void {
this._observers.push(observer);
}
public UnregisterObserver(observer: IWarehouseObserver): void {
// Array remove
this._observers.splice(_.indexOf(this._observers, observer), 1);
}
public NotifyThingTypeDefine(type: ThingType, sender: string=null): void {
_.each(this._observers, (observer) => {
observer.Define(type, sender);
});
}
public NotifyThingUpdate(thing: Thing, sender: string=null): void {
_.each(this._observers, (observer) => {
observer.Updated(thing, sender);
});
}
public NotifyThingCreation(thing: Thing, sender: string=null): void {
_.each(this._observers, (observer) => {
observer.New(thing, sender);
});
}
public NotifyThingDeleted(thing: Thing, sender: string= null): void {
_.each(this._observers, (observer) => {
observer.Deleted(thing, sender);
});
}
public GetThing(id: string): Thing {
return this._things[id];
}
public GetThingType(name: string): ThingType {
return this._thingTypes[name];
}
public get Things(): Thing[] {
return _.values(this._things);
}
public get ThingsTypes(): ThingType[] {
return _.values(this._thingTypes);
}
}
}