Skip to content
Snippets Groups Projects
aggregator.js 18.2 KiB
Newer Older
Stefanie Lemcke's avatar
Stefanie Lemcke committed
/**
 * This module representing a Context Aggregator. 
 * It aggregates data from multiple widgets.
 * 
 * @module Aggregator
 * @fileOverview
 */
define(['easejs', 'MathUuid','widget', 'widgetHandle', 'widgetHandleList', 
        'attributeType', 'attributeValue', 'attributeValueList', 'subscriber', 
        'subscriberList', 'callbackList', 'storage'],
 	function( easejs, MathUuid, Widget, WidgetHandle,WidgetHandleList, AttributeType,
 			AttributeValue, AttributeValueList, Subscriber, SubscriberList,
 			CallbackList, Storage){

 	var Class = easejs.Class;
 	var AbstractClass = easejs.AbstractClass;
	var Aggregator =  AbstractClass('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
		 * @type {WidgetHandleList}
		 * @memberof Aggregator#
		 * @desc List of subscribed Widgets.
		 */
		'protected widgets' : [],		
Tobias Moebert's avatar
Tobias Moebert committed

        'protected interpreters' : [],

Stefanie Lemcke's avatar
Stefanie Lemcke committed
		/**
		 * @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
		 * @requires WidgetHandle
		 * @requires WidgetHandleList
		 * @constructs Aggregator
		 */
		'override virtual public __construct': function(_discoverer)
Stefanie Lemcke's avatar
Stefanie Lemcke committed
        {
			this.id = Math.uuid();
			this.widgets = new WidgetHandleList();
			this.initWidgetHandles();
Tobias Moebert's avatar
Tobias Moebert committed
            this.interpreters = [];
			this.__super(_discoverer);
Stefanie Lemcke's avatar
Stefanie Lemcke committed
			this.aggregatorSetup();
        },
        
        /**
		 * 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);
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		
		/**
		 * Sets WidgetHandles.
		 * 
		 * @protected
	   	 * @alias setWidgets
		 * @memberof Aggregator#
		 * @param {(WidgetHandleList|Array)} _widgetList List of WidgetHandles
	     */
		'protected setWidgets' : function(_widgetList){
			this.widgets = new WidgetHandleList().withItems(_widgetList);		
		},
		
		/**
		 * Adds WidgetHandle.
		 * 
		 * @public
	   	 * @alias addWidget
		 * @memberof Aggregator#
		 * @param {WidgetHandle} _widget WidgetHandle
	     */
		'public addWidget' : function(_widget){
			this.widgets.put(_widget);
		},
		
		/**
		 * Returns the available WidgetHandles.
		 * 
		 * @public
		 * @alias getWidgets
		 * @memberof Aggregator#
		 * @returns {WidgetHandleList}
		 */
		'public getWidgets' : function() {
			return this.widgets;
		},
		
		/**
		 * Removes WidgetHandle from list.
		 * 
		 * @protected
	   	 * @alias removeWidget
		 * @memberof Aggregator#
		 * @param {String} _key Id of the WidgetHandle
	     */
		'protected removeWidget' : function(_key){
			this.widgets.removeItem(_key);
		},
		
		/**
		 * Retrieves all Attributes of the specified widgets.
         * If the defined name in WidgetHandle does not match the name of the 
         * returned instance, the WidgetHandle will be removed from the list.
		 * 
		 * @protected
	   	 * @alias initAttributes
		 * @memberof Aggregator#
	     */
		'protected initAttributes' : function(){
			if(this.widgets.size() > 0){
				var widgetList = this.widgets.getItems();
				for(var i in widgetList){
					var widgetHandle = widgetList[i];
					var widgetInstance = this.discoverer.getComponent(widgetHandle.getId());
					if(widgetInstance && widgetInstance.getName() === widgetHandle.getName()){
						this.setAttributes(widgetInstance.queryAttributes());
					} else {
						this.removeWidget(widgetHandle.getName());
					}
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		
		/**
		 * Retrieves all ConstantAttributes of the specified widgets.
         * If the defined name in WidgetHandle does not match the name of the 
         * returned instance, the WidgetHandle will be removed from the list.
		 * 
		 * @protected
	   	 * @alias initConstantAttributes
		 * @memberof Aggregator#
	     */
		'protected initConstantAttributes' : function(){
			if(this.widgets.size() > 0){
				var widgetList = this.widgets.getItems();
				for(var i in widgetList){
					var widgetHandle = widgetList[i];					
					var widgetInstance = this.discoverer.getComponent(widgetHandle.getid());
					if(widgetInstance && widgetInstance.getName() === widgetHandle.getName()){
						this.setConstantAttributes(widgetInstance.queryConstantAttributes());
					} else {
						this.removeWidget(widgetHandle.getName());
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		
		/**
		 * Retrieves all actual Callbacks of the specified Widgets.
		 * 
		 * @protected
	   	 * @alias initCallbacks
		 * @memberof Aggregator#
	     */
		'protected initCallbacks' : function(){
			if(this.widgets.size() > 0){
				var widgetList = this.widgets.getItems();
				for(var i in widgetList){
					var widgetHandle = widgetList[i];
					this.initWidgetSubscription(widgetHandle);
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		
		/**
		 * InitMethod for Aggregators. Called by constructor.
		 * Initializes the associated Storage.
		 * 
		 * @protected
	   	 * @alias aggregatorSetup
		 * @memberof Aggregator#
	     */
		'protected aggregatorSetup' : function(){
			this.initStorage('DB_'+ this.name);
			this.setAggregatorAttributeValues();
			this.setAggregatorConstantAttributeValues();
			this.setAggregatorCallbacks();
		},
		
		/**
		 * Initializes the Widget that should be subscribed.
		 * Called by aggregatorSetup().
		 * 
		 * @function
		 * @abstract
		 * @protected
		 * @alias initWidgetHandles
		 * @memberof Aggregator#
		 */
		'abstract protected initWidgetHandles' : [],
		
		/**
		 * Initializes the provided attributeValues that are only specific to the Aggregator.
		 * Called by aggregatorSetup().
		 * 
		 * @function
		 * @abstract
		 * @protected
		 * @alias setAggregatorAttributeValues
		 * @memberof Aggregator#
		 */
		'abstract protected setAggregatorAttributeValues' : [],
		/**
		 * Initializes the provided ConstantAttributeValues that are only specific to the Aggregator.
		 * Called by aggregatorSetup().
		 * 
		 * @function
		 * @abstract
		 * @protected
		 * @alias setAggregatorConstantAttributeValues
		 * @memberof Aggregator#
		 */
		'abstract protected setAggregatorConstantAttributeValues' : [],
		/**
		 * Initializes the provided Callbacks that are only specific to the Aggregator.
		 * Called by aggregatorSetup().
		 * 
		 * @function
		 * @abstract
		 * @protected
		 * @alias setAggregatorCallbacks
		 * @memberof Aggregator#
		 */
		'abstract protected setAggregatorCallbacks' : [],

		/**
		 * 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).
									withConditions(_conditions);	
				console.log(this.name + ' subscribeTo: ' + _widget.getName());
				_widget.addSubscriber(subscriber);
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		
		/**
		 * Subscribes to the widgets that are defined in the WidgetHandleList
         * used in the initCallback method.
         * If the defined name in WidgetHandle does not match the name of the 
         * returned instance, the WidgetHandle will be removed from the list.
		 * 
		 * 
		 * @protected
	   	 * @alias initWidgetSubscription
		 * @memberof Aggregator#
		 * @param {WidgetHandle} _widgetHandle Widget that should be subscribed.
		 * @returns {?CallbackList}
	     */
		'protected initWidgetSubscription' : function(_widgetHandle){
			var calls = null;
			if(Class.isA(WidgetHandle, _widgetHandle)){				
				var widget = this.discoverer.getComponent(_widgetHandle.getId());
				if(widget && widget.getName() === _widgetHandle.getName()){
					//subscribe to all callbacks
					calls = widget.queryCallbacks();
					this.subscribeTo(widget, calls);
				} else {
					this.removeWidget(_widgetHandle.getName());
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		},
		
		/**
		 * Adds the specified callbacks of a widget to the aggregator.
Stefanie Lemcke's avatar
Stefanie Lemcke committed
         * 
		 * @public
	   	 * @alias addWidgetSubscription
		 * @memberof Aggregator#
		 * @param {WidgetHandle|Widget} _widgetHandleOrWidget Widget that should be subscribed.
		 * @param {CallbackList} _callbackList required Callbacks
Stefanie Lemcke's avatar
Stefanie Lemcke committed
	     */
		'public addWidgetSubscription' : function(_widgetHandleOrWidget, _callbackList){
            if (Class.isA(Widget, _widgetHandleOrWidget)) {
                if (!_callbackList || !Class.isA(CallbackList, _callbackList)) {
                    _callbackList = _widgetHandleOrWidget.getCallbackList();
                }
                _widgetHandleOrWidget = _widgetHandleOrWidget.getHandle();
            }
			if(Class.isA(WidgetHandle, _widgetHandleOrWidget) && Class.isA(CallbackList, _callbackList)){
				var widget = this.discoverer.getComponent(_widgetHandleOrWidget.getId());
				if(widget && widget.getName() === _widgetHandleOrWidget.getName()){
Stefanie Lemcke's avatar
Stefanie Lemcke committed
					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(_widgetHandleOrWidget);
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		
		/**
		 * Removes subscribed Widgets and deletes the entry 
		 * for subscribers in the associated Widget.
		 * 
		 * @public
	   	 * @alias unsubscribeFrom
		 * @memberof Aggregator#
		 * @param {WidgetHandle} _widgetHandle Widget that should be removed.
	     */
		'public unsubscribeFrom' : function(_widgetHandle){
			if(Class.isA(WidgetHandle, _widgetHandle)){
				var widget = this.discoverer.getComponent(_widgetHandle.getId());
				if(widget && widget.getName() === _widgetHandle.getName()){
					console.log('aggregator unsubscribeFrom: ' + widget.getName());
					widget.removeSubscriber(this.id);
					this.widgets.removeItem(_widgetHandle.getName());
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		
		/**
		 * 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){
Stefanie Lemcke's avatar
Stefanie Lemcke committed
			if(_data instanceof Array){
				list = _data;
			} else if (Class.isA(AttributeValueList, _data)) {
Stefanie Lemcke's avatar
Stefanie Lemcke committed
				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);
					}
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		
		/**
		 * 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, _data, _function){
			var interpreter = this.discoverer.getComponent(_interpreterId);
			if(interpreter){
				interpreter.callInterpreter(_data, _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);
			if(interpreter){
				response = interpreter.getInterpretedData();
				var list = response.getOutAttributes().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);
						}
Stefanie Lemcke's avatar
Stefanie Lemcke committed
			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();
		},
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		/**
		 * Only actualizes the attributeType cache in th database.
		 * For an alternativ action can be used a callback.
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		 * @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 handle and calls the callback afterwards.
         *
         * @public
         * @virtual
         * @alias queryReferencedWidget
         * @memberof Aggregator#
         * @param {WidgetHandle} _widgetHandle The handle of the widget to query.
         * @param {Callback} _callback The callback to query after the widget was updated.
         */
        'virtual public queryReferencedWidget' :function(_widgetHandle, _callback){
            var widget = this.discoverer.getWidget(_widgetHandle.getId());
            widget.updateWidgetInformation(_callback);
        },

        /**
         * Updates all the widgets referenced by the aggregator and calls the callback afterwards.
         *
         * @param {Callback} _callback The callback to query after all the widget where updated.
         */
        'virtual public queryReferencedWidgets': function(_callback) {
            var self = this;
            var completedQueriesCounter = 0;
            var referencedWidgetHandles = this.getWidgets().getItems();
            for (var index in referencedWidgetHandles) {
                var theWidgetHandle = referencedWidgetHandles[index];
                this.queryReferencedWidget(theWidgetHandle, function () {
                    completedQueriesCounter++;
                    if (completedQueriesCounter == self.widgets.size()) {
                        if (_callback && typeof(_callback) == 'function') {
                            _callback();
                        }
                    }
                });
            }
Tobias Moebert's avatar
Tobias Moebert committed
        },

        'public addInterpreter': function(_theInterpreter) {
            this.interpreters.push(_theInterpreter.getId());
        },

        'public getInterpreters': function() {
            return this.interpreters;
Stefanie Lemcke's avatar
Stefanie Lemcke committed

	return Aggregator;
});