Newer
Older
/**
* This module representing a Context Aggregator.
* It aggregates data from multiple widgets.
*
* @module Aggregator
* @fileOverview
*/
define(['easejs', 'MathUuid','widget',
'attributeType', 'attributeValue', 'attributeValueList', 'subscriber',
'subscriberList', 'callbackList', 'storage', 'widgetDescription', 'interpreter', 'attributeTypeList'],
function(easejs, MathUuid, Widget, AttributeType,
AttributeValue, AttributeValueList, Subscriber, SubscriberList,
CallbackList, Storage, WidgetDescription, Interpreter, AttributeTypeList){
var Aggregator = Class('Aggregator').
extend(Widget,
{
/**
* @alias name
* @public
* @type {string}
* @memberof Aggregator#
* @desc Name of the Widget.
*/
'public name' : 'Aggregator',
/**
* @alias id
* @public
* @type {string}
* @memberof Aggregator#
* @desc ID of the Aggregator. Will be generated.
*/
'public id' : '',
/**
* @alias widgets
* @protected
* @desc List of subscribed widgets referenced by ID.
/**
* @alias interpreters
* @protected
* @type {Array}
* @memberof Aggregator#
* @desc List of subscribed interpreters referenced by ID.
*/
/**
* @alias db
* @protected
* @type {Storage}
* @memberof Aggregator#
* @desc Database of the Aggregator.
*/
'protected db' : '',
/**
* Constructor: Generates the id and initializes the Aggregator.
*
* @abstract
* @class Aggregator
* @extends Widget
* @classdesc The Widget handles the access to sensors.
* @requires easejs
* @requires MathUuid
* @requires CallbackList
* @requires AttributeType
* @requires AttributeValue
* @requires AttributeValueList
* @requires Subscriber
* @requires SubscriberList
* @requires Storage
* @requires Widget
* @constructs Aggregator
*/
'override virtual public __construct': function(_discoverer, _attributeTypes)
this.__super(_discoverer, _attributeTypes);
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
},
/**
* Returns the type of this class, in this case
* "Aggregator".
*
* @override
* @public
* @alias getType
* @memberof Aggregator#
* @returns {string}
*/
'override public getType' : function(){
return 'Aggregator';
},
/**
* Adds new AttributeTypes, useful when a new Widget is subscribed.
*
* @protected
* @alias addAttributeType
* @memberof Aggregator#
* @param {AttributeType} _attributeType attributeType
*/
'protected addAttributeType' : function(_attributeType){
if(Class.isA( AttributeType, _attributeType )){
this.attributeTypes.put(_attributeType);
var attVal = new AttributeValue().buildFromAttributeType(_attributeType);
this.attributes.put(attVal);
*
* @protected
* @alias setWidgets
* @memberof Aggregator#
* @param {Array} _widgetIds List of Widget IDs
'protected setWidgets' : function(_widgetIds){
this.widgets = _widgetIds;
*
* @public
* @alias addWidget
* @memberof Aggregator#
* @param {String|Widget} _widgetIdOrWidget Widget ID
'public addWidget' : function(_widgetIdOrWidget){
if (Class.isA(Widget, _widgetIdOrWidget)) {
this.widgets.push(_widgetIdOrWidget.getId());
} else if(typeof _widgetIdOrWidget == "string") {
this.widgets.push(_widgetIdOrWidget);
}
* Returns the available Widget IDs.
*
* @public
* @alias getWidgets
* @memberof Aggregator#
*/
'public getWidgets' : function() {
return this.widgets;
},
/**
* Removes Widget ID from list.
*
* @protected
* @alias removeWidget
* @memberof Aggregator#
* @param {String} _widgetId Id of the Widget
'protected removeWidget' : function(_widgetId){
var index = this.widgets.indexOf(_widgetId);
if (index > -1) {
this.widgets = this.widgets.splice(index, 1);
}
},
/**
* Retrieves all Attributes of the specified widgets.
*
* @protected
* @alias initAttributes
* @memberof Aggregator#
*/
'protected initAttributes' : function(){
if(this.widgets.length > 0){
var widgetIdList = this.widgets;
for(var i in widgetIdList){
var widgetId = widgetIdList[i];
var widgetInstance = this.discoverer.getComponent(widgetId);
if (widgetInstance) {
this.setAttributes(widgetInstance.queryAttributes());
}
/**
* Retrieves all ConstantAttributes of the specified widgets.
*
* @protected
* @alias initConstantAttributes
* @memberof Aggregator#
*/
'protected initConstantAttributes' : function(){
if(this.widgets.length > 0){
var widgetIdList = this.widgets;
for(var i in widgetIdList){
var widgetId = widgetIdList[i];
var widgetInstance = this.discoverer.getComponent(widgetId);
if (widgetInstance) {
this.setConstantAttributes(widgetInstance.queryConstantAttributes());
/**
* Retrieves all actual Callbacks of the specified Widgets.
*
* @protected
* @alias initCallbacks
* @memberof Aggregator#
*/
'protected initCallbacks' : function(){
if(this.widgets.length > 0){
var widgetIdList = this.widgets;
for(var i in widgetIdList){
var widgetId = widgetIdList[i];
this.initWidgetSubscription(widgetId);
/**
* Start the setup of the aggregator after the initialisation has finished.
*
* @public
* @alias didFinishInitialization
* @memberof Aggregator#
* @param _attributeTypes
*/
'override public didFinishInitialization': function(_attributeTypes) {
this.aggregatorSetup(_attributeTypes);
},
/**
* InitMethod for Aggregators. Called by constructor.
* Initializes the associated Storage.
*
* @protected
* @alias aggregatorSetup
* @memberof Aggregator#
*/
'protected aggregatorSetup' : function(_attributeTypes){
this.initStorage('DB_'+this.name);
this.setAggregatorAttributeValues(_attributeTypes);
this.setAggregatorConstantAttributeValues();
this.setAggregatorCallbacks();
},
/**
* Initializes the provided attributeValues that are only specific to the Aggregator.
* Called by aggregatorSetup().
*
* @function
* @abstract
* @protected
* @alias setAggregatorAttributeValues
* @memberof Aggregator#
*/
'virtual protected setAggregatorAttributeValues' : function(_attributeTypes) {
for (var index in _attributeTypes) {
var theAttributeType = _attributeTypes[index];
this.addAttribute(new AttributeValue().buildFromAttributeType(theAttributeType));
}
},
/**
* Initializes the provided ConstantAttributeValues that are only specific to the Aggregator.
* Called by aggregatorSetup().
*
* @function
* @abstract
* @protected
* @alias setAggregatorConstantAttributeValues
* @memberof Aggregator#
*/
'virtual protected setAggregatorConstantAttributeValues' : function() {
},
/**
* Initializes the provided Callbacks that are only specific to the Aggregator.
* Called by aggregatorSetup().
*
* @function
* @abstract
* @protected
* @alias setAggregatorCallbacks
* @memberof Aggregator#
*/
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
'virtual protected setAggregatorCallbacks' : function() {
},
/**
* Adds an interpreter to the aggregator.
*
* @public
* @alias addInterpreter
* @memberof Aggregator#
* @param _theInterpreter
*/
'public addInterpreter': function(_theInterpreter) {
this.interpreters.push(_theInterpreter.getId());
},
/**
* Returns an array with the UUIDs of the interpreters that where added to the aggregator.
*
* @public
* @alias getInterpreters
* @memberof Aggregator#
* @returns {Array} The UUIDs of the connected interpreters.
*/
'public getInterpreters': function() {
return this.interpreters;
},
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
/**
* Returns the current Attributes that are saved in the cache.
*
* @public
* @alias getCurrentData
* @memberof Aggregator#
* @returns {AttributeValueList}
*/
'public getCurrentData' : function(){
var response = new AttributeValueList();
response.putAll(this.attributes);
return response;
},
/**
* Subscribes to the given widget for the specified Callbacks.
*
* @protected
* @alias subscribeTo
* @memberof Aggregator#
* @param {Widget} _widget Widget that should be subscribed to.
* @param {CallbackList} _callbacks required Callbacks
*/
'protected subscribeTo' : function(_widget, _callbacks, _subSet, _conditions){
if(Class.isA(Widget, _widget)){
var subscriber = new Subscriber().withSubscriberId(this.id).
withSubscriberName(this.name).
withSubscriptionCallbacks(_callbacks).
withAttributesSubset(_subSet).
* Subscribes to the widgets that are defined in the Widget ID List
* used in the initCallback method.
*
* @protected
* @alias initWidgetSubscription
* @memberof Aggregator#
* @param {String} _widgetId Widget that should be subscribed.
'protected initWidgetSubscription' : function(_widgetId){
if(Class.isA(String, _widgetId)){
var widget = this.discoverer.getComponent(_widgetId);
if (widget){
//subscribe to all callbacks
calls = widget.queryCallbacks();
this.subscribeTo(widget, calls);
* Adds the specified callbacks of a widget to the aggregator.
*
* @public
* @alias addWidgetSubscription
* @memberof Aggregator#
* @param {String|Widget|WidgetDescription} _widgetIdOrWidget Widget that should be subscribed.
* @param {CallbackList} _callbackList required Callbacks
'public addWidgetSubscription' : function(_widgetIdOrWidget, _callbackList){
if (Class.isA(Widget, _widgetIdOrWidget) || Class.isA(WidgetDescription, _widgetIdOrWidget)) {
if (Class.isA(Widget, _widgetIdOrWidget) && (!_callbackList || !Class.isA(CallbackList, _callbackList))) {
_callbackList = _widgetIdOrWidget.getCallbackList();
}
_widgetIdOrWidget = _widgetIdOrWidget.getId();
}
if(typeof _widgetIdOrWidget == "string" && Class.isA(CallbackList, _callbackList)){
var widget = this.discoverer.getComponent(_widgetIdOrWidget);
if (widget) {
this.subscribeTo(widget, _callbackList);
this.callbacks.putAll(_callbackList);
var callsList = _callbackList.getItems();
for(var x in callsList){
var singleCallback = callsList[x];
var typeList = singleCallback.getAttributeTypes().getItems();
for(var y in typeList){
var singleType = typeList[y];
this.addAttributeType(singleType);
this.addWidget(_widgetIdOrWidget);
/**
* Removes subscribed Widgets and deletes the entry
* for subscribers in the associated Widget.
*
* @public
* @alias unsubscribeFrom
* @memberof Aggregator#
* @param {String} _widgetId Widget that should be removed.
'public unsubscribeFrom' : function(_widgetId){
if(typeof _widgetId == "string"){
var widget = this.discoverer.getComponent(_widgetId);
if (widget) {
console.log('aggregator unsubscribeFrom: ' + widget.getName());
widget.removeSubscriber(this.id);
/**
* Puts context data to Widget and expects an array.
*
* @override
* @public
* @alias putData
* @memberof Aggregator#
* @param {(AttributeValueList|Array)} _data data that shall be input
*/
'override public putData' : function(_data){
} else if (Class.isA(AttributeValueList, _data)) {
list = _data.getItems();
}
for(var i in list){
var x = list[i];
if(Class.isA( AttributeValue, x ) && this.isAttribute(x)){
this.addAttribute(x);
if(this.db){
this.store(x);
}
/**
* Calls the given Interpreter for interpretation the data.
*
* @public
* @alias interpretData
* @memberof Aggregator#
* @param {String} _interpreterId ID of the searched Interpreter
* @param {(AttributeValueList|Array)} _data data that should be interpreted
* @param {?function} _function for additional actions, if an asynchronous function is used
*/
'public interpretData' : function(_interpreterId, _function){
var interpreter = this.discoverer.getComponent(_interpreterId);
if (Class.isA(Interpreter, interpreter)) {
interpreter.callInterpreter(this.getAttributeValues(interpreter.getInAttributeTypes()), _function);
}
},
/**
* Calls the given Interpreter for getting the data.
*
* @public
* @alias getInterpretedData
* @memberof Aggregator#
* @param {String} _interpreterId ID of the searched Interpreter
* @returns {?AttributeValueList}
*/
'public getInterpretedData' : function(_interpreterId){
var response = 'undefined';
var interpreter = this.discoverer.getComponent(_interpreterId);
var attributeList = response.getOutAttributes().getItems();
for (var i in attributeList) {
var theAttribute = attributeList[i];
if (Class.isA(AttributeValue, theAttribute) && this.isAttribute(theAttribute)) {
this.addAttribute(theAttribute);
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
return response;
},
/**
* Initializes the database with the specified name.
*
* @protected
* @alias initStorage
* @memberof Aggregator#
* @param {String} _name Name of the Storage
*/
'protected initStorage' : function(_name){
this.db = new Storage(_name, 7200000, 5);
},
/**
* Stores the data.
*
* @protected
* @alias store
* @memberof Aggregator#
* @param {AttributeValue} _attributeValue data that should be stored
*/
'protected store' : function(_attributeValue){
this.db.store(_attributeValue);
},
/**
* Queries the database and returns the last retrieval result.
* It may be that the retrieval result is not up to date,
* because an asynchronous function is used for the retrieval.
* For retrieving the current data, this function can be used as callback function
* in retrieveStorage().
*
* @public
* @alias queryAttribute
* @memberof Aggregator#
* @param {String} _name Name of the searched AtTributes.
* @param {?function} _function for alternative actions, because an asynchronous function is used
*/
'public queryAttribute' : function(_name, _function){
this.db.retrieveAttributes(_name, _function);
},
/**
* Queries a specific table and only actualizes the storage cache.
* For an alternativ action can be used a callback.
*
* @public
* @alias retrieveStorage
* @memberof Aggregator#
* @returns {RetrievalResult}
*/
'public retrieveStorage' : function(){
return this.db.getCurrentData();
},
/**
* Returns an overview about the stored attributes.
* It may be that the overview about the stored attributes is not up to date,
* because an asynchronous function is used for the retrieval.
* For retrieving the current data, this function can be used as callback function
* in queryTables().
*
* @public
* @alias getStorageOverview
* @memberof Aggregator#
* @returns {?Array}
*/
'public getStorageOverview' : function(){
return this.db.getAttributesOverview();
},
/**
* Only actualizes the attributeType cache in th database.
* For an alternativ action can be used a callback.
* @public
* @alias queryTables
* @memberof Aggregator#
* @param {?function} _function for alternative actions, because an asynchronous function is used
*/
'public queryTables' : function(_function){
this.db.getAttributeNames(_function);
},
/**
* Updates the information for the widget with the provided ID and calls the callback afterwards.
*
* @public
* @virtual
* @alias queryReferencedWidget
* @memberof Aggregator#
* @param {String} _widgetId The ID of the widget to query.
* @param {Callback} _callback The callback to query after the widget was updated.
*/
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
'virtual public queryReferencedWidget' :function(_widgetId, _callback){
this.discoverer.getWidget(_widgetId).updateWidgetInformation(_callback);
},
/**
* Returns the UUIDs of all connected widgets and interpreters.
*
* @private
* @alias getComponentUUIDs
* @memberof Aggregator#
* @returns {Array.<T>} The UUIDs.
*/
'private getComponentUUIDs': function() {
return this.widgets.concat(this.interpreters);
},
/**
* Return true if a component with the provided UUID was connected to the aggregator.
*
* @private
* @alias hasComponent
* @memberof Aggregator#
* @param {String} uuid The UUID of the component to check.
* @returns {boolean}
*/
'private hasComponent': function(uuid) {
return jQuery.inArray(uuid, this.getComponentUUIDs()) != -1;
},
/**
*
* @private
* @alias doesSatisfyAttributeType
* @param _attributeType
* @returns {boolean}
*/
'private doesSatisfyAttributeType': function(_attributeType) {
var componentUUIDs = this.getComponentUUIDs();
var doesSatisfy = false;
for (var index in componentUUIDs) {
var theComponent = this.discoverer.getComponent(componentUUIDs[index]);
if (theComponent.getDescription().doesSatisfyAttributeType(_attributeType)) {
doesSatisfy = true;
}
}
return doesSatisfy;
},
/**
* Searches for components that can satisfy the requested attributes. Through recursion it is possible to search
* for components that satisfy attributes of components that have been found in the process.
*
* @private
* @alias getComponentsForUnsatisfiedAttributeTypes
* @memberof Aggregator#
* @param {AttributeTypeList} _unsatisfiedAttributes A list of attributes that components should be searched for.
* @param {boolean} _all If true all attributes must be satisfied by a single component.
* @param {Array} _componentTypes An array of components classes that should be searched for (e.g. Widget, Interpreter and Aggregator).
*/
'private getComponentsForUnsatisfiedAttributeTypes': function(_unsatisfiedAttributes, _all, _componentTypes) {
// ask the discoverer for components that satisfy the requested components
var relevantComponents = this.discoverer.getComponentsByAttributes(_unsatisfiedAttributes, _all, _componentTypes);
console.log("I found "+relevantComponents.length+" component(s) of type "+_componentTypes+" that might satisfy the requested attributes.");
// iterate over all found components
for(var index in relevantComponents) {
// get the component
var theComponent = relevantComponents[index];
console.log("Let's look at component "+theComponent.getName()+".");
// if the component was added before, ignore it
if (!this.hasComponent(theComponent.getId())) {
var outAttributes = theComponent.getDescription().getOutAttributeTypes().getItems();
// if component is a widget and it wasn't added before, subscribe to its callbacks
if (Class.isA(Widget, theComponent)) {
console.log("It's a widget.");
this.addWidgetSubscription(theComponent);
// remove satisfied attributes
for (var widgetOutAttributeIndex in outAttributes) {
var widgetOutAttribute = outAttributes[widgetOutAttributeIndex];
// add the attribute type to the aggregators list of handled attribute types
if (!this.getAttributeTypes().contains(widgetOutAttribute)) this.addAttributeType(widgetOutAttribute);
console.log("I can now satisfy attribute "+widgetOutAttribute.getIdentifier()+" with the help of "+theComponent.getName()+"! That was easy :)");
_unsatisfiedAttributes.removeItem(widgetOutAttribute.getIdentifier());
}
} else if (Class.isA(Interpreter, theComponent)) { // if the component is an interpreter and all its in attributes can be satisfied, add the interpreter
console.log("It's an interpreter.");
var inAttributes = theComponent.getInAttributeTypes().getItems();
var canSatisfyInAttributes = true;
// iterate over the attributes needed to satisfy the interpreter
for (var inAttributeIdentifier in inAttributes) {
// get the attribute
var theInAttribute = inAttributes[inAttributeIdentifier];
console.log("The interpreter needs the attribute "+theInAttribute.getIdentifier()+".");
// if required attribute is not already satisfied by the aggregator search for components that do
if (!this.doesSatisfyAttributeType(theInAttribute)) {
console.log("It seems that I can't satisfy "+theInAttribute.getIdentifier()+", but I will search for components that can.");
var newAttributeList = new AttributeTypeList();
newAttributeList.put(theInAttribute);
this.getComponentsForUnsatisfiedAttributeTypes(newAttributeList, false, [Widget, Interpreter]);
// if the attribute still can't be satisfied drop the interpreter
if (!this.doesSatisfyAttributeType(theInAttribute)) {
console.log("I couldn't find a component to satisfy "+theInAttribute.getIdentifier()+". Dropping interpreter "+theComponent.getName()+". Bye bye.");
canSatisfyInAttributes = false;
break;
}
} else {
console.log("It seems that I already satisfy the attribute "+theInAttribute.getIdentifier()+". Let's move on.");
}
}
if (canSatisfyInAttributes) {
this.addInterpreter(theComponent);
// remove satisfied attribute
for (var interpreterOutAttributeIndex in outAttributes) {
var interpreterOutAttribute = outAttributes[interpreterOutAttributeIndex];
// add the attribute type to the aggregators list of handled attribute types
if (!this.getAttributeTypes().contains(interpreterOutAttribute)) this.addAttributeType(interpreterOutAttribute);
console.log("I can now satisfy attribute "+interpreterOutAttribute.getIdentifier()+" with the help of "+theComponent.getName()+"! Great!");
_unsatisfiedAttributes.removeItem(interpreterOutAttribute.getIdentifier());
}
} else {
console.log("Found interpreter but can't satisfy required attributes.");
for (var j in theComponent.getDescription().getInAttributeTypes().getItems()) {
console.log("Missing "+theComponent.getDescription().getInAttributeTypes().getItems()[j].getIdentifier()+".");
}
}
}
} else {
console.log("Aggregator already has component "+theComponent.getName()+". Nothing to do here ;)");
}
}
},
/**
* After the aggregator finished its setup start searching for component that satisfy the attributes that where requrested.
*
* @public
* @virtual
* @alias didFinishSetup
* @memberof Aggregator#
*/
'virtual public didFinishSetup': function() {
unsatisfiedAttributes = this.getAttributeTypes().clone();
// get all widgets that satisfy attribute types
this.getComponentsForUnsatisfiedAttributeTypes(unsatisfiedAttributes, false, [Widget]);
// get all interpreters that satisfy attribute types
this.getComponentsForUnsatisfiedAttributeTypes(unsatisfiedAttributes, false, [Interpreter]);
//console.log(unsatisfiedAttributes);
//console.log(this.attributeTypes);
},
/**
* Updates all the widgets referenced by the aggregator and calls the provided callback afterwards.
* @public
* @virtual
* @alias queryReferencedWidgets
* @memberof Aggregator#
* @param {Function} _callback The callback to query after all the widget where updated.
*/
'virtual public queryReferencedWidgets': function(_callback) {
var self = this;
var completedQueriesCounter = 0;
if (this.widgets.length > 0) {
for (var index in this.widgets) {
var theWidgetId = this.widgets[index];
this.queryReferencedWidget(theWidgetId, function () {
completedQueriesCounter++;
if (completedQueriesCounter == self.widgets.length) {
if (_callback && typeof(_callback) == 'function') {
_callback(self.getAttributeValues());
}
});
}
} else {
if (_callback && typeof(_callback) == 'function') {
_callback(self.getAttributeValues());
}
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
/**
* Let's all connected interpreters interpret data.
*
* @public
* @alias queryReferencedInterpreters
* @memberof Aggregator#
* @param {Function} _callback The callback to query after all the interpreters did interpret data.
*/
'public queryReferencedInterpreters': function(_callback) {
var self = this;
var completedQueriesCounter = 0;
if (this.interpreters.length > 0) {
for(var index in this.interpreters) {
var theInterpreterId = this.interpreters[index];
self.interpretData(theInterpreterId, function() {
self.getInterpretedData(theInterpreterId);
completedQueriesCounter++;
if (completedQueriesCounter == self.interpreters.length) {
if (_callback && typeof(_callback) == 'function') {
_callback(self.getAttributeValues());
}
}
});
}
} else {
if (_callback && typeof(_callback) == 'function') {
_callback(self.getAttributeValues());
}
}
/**
* Query all referenced widgets and afterwards all connected interpreters.
*
* @public
* @alias queryReferencedComponents
* @memberof Aggregator#
* @param {Function} _callback the callback to query after all components did finish their work.
*/
'public queryReferencedComponents': function(_callback) {
var self = this;
this.queryReferencedWidgets(function(_attributeValues) {
self.queryReferencedInterpreters(function(_attributeValues) {
if (_callback && typeof(_callback) == 'function') {
_callback(_attributeValues);
}
});
});