Skip to content
Snippets Groups Projects
aggregator.js 28.8 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',
Stefanie Lemcke's avatar
Stefanie Lemcke committed
        'attributeType', 'attributeValue', 'attributeValueList', 'subscriber', 
        'subscriberList', 'callbackList', 'storage', 'widgetDescription', 'interpreter', 'attributeTypeList'],
 	function(easejs, MathUuid, Widget, AttributeType,
Stefanie Lemcke's avatar
Stefanie Lemcke committed
 			AttributeValue, AttributeValueList, Subscriber, SubscriberList,
 			CallbackList, Storage, WidgetDescription, Interpreter, AttributeTypeList){
Stefanie Lemcke's avatar
Stefanie Lemcke committed

 	var Class = easejs.Class;
	var Aggregator =  Class('Aggregator').
Stefanie Lemcke's avatar
Stefanie Lemcke committed
				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 {Array}
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		 * @memberof Aggregator#
		 * @desc List of subscribed widgets referenced by ID.
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		 */
		'protected widgets' : [],
Tobias Moebert's avatar
Tobias Moebert committed

        /**
         * @alias interpreters
         * @protected
         * @type {Array}
         * @memberof Aggregator#
         * @desc List of subscribed interpreters referenced by ID.
         */
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
		 * @constructs Aggregator
		 */
		'override virtual public __construct': function(_discoverer, _attributeTypes)
Stefanie Lemcke's avatar
Stefanie Lemcke committed
        {
			this.id = Math.uuid();
			this.widgets = [];
Tobias Moebert's avatar
Tobias Moebert committed
            this.interpreters = [];
			this.__super(_discoverer, _attributeTypes);
Stefanie Lemcke's avatar
Stefanie Lemcke committed
        },
        
        /**
		 * 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 Widget IDs.
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		 * 
		 * @protected
	   	 * @alias setWidgets
		 * @memberof Aggregator#
		 * @param {Array} _widgetIds List of Widget IDs
Stefanie Lemcke's avatar
Stefanie Lemcke committed
	     */
		'protected setWidgets' : function(_widgetIds){
			this.widgets = _widgetIds;
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		},
		
		/**
		 * Adds Widget ID.
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		 * 
		 * @public
	   	 * @alias addWidget
		 * @memberof Aggregator#
		 * @param {String|Widget} _widgetIdOrWidget Widget ID
Stefanie Lemcke's avatar
Stefanie Lemcke committed
	     */
		'public addWidget' : function(_widgetIdOrWidget){
            if (Class.isA(Widget, _widgetIdOrWidget)) {
                this.widgets.push(_widgetIdOrWidget.getId());
            } else if(typeof _widgetIdOrWidget == "string") {
                this.widgets.push(_widgetIdOrWidget);
            }
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		},
		
		/**
		 * Returns the available Widget IDs.
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		 * 
		 * @public
		 * @alias getWidgets
		 * @memberof Aggregator#
		 * @returns {Array}
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		 */
		'public getWidgets' : function() {
			return this.widgets;
		},
		
		/**
		 * Removes Widget ID from list.
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		 * 
		 * @protected
	   	 * @alias removeWidget
		 * @memberof Aggregator#
		 * @param {String} _widgetId Id of the Widget
Stefanie Lemcke's avatar
Stefanie Lemcke committed
	     */
		'protected removeWidget' : function(_widgetId){
            var index = this.widgets.indexOf(_widgetId);
            if (index > -1) {
                this.widgets = this.widgets.splice(index, 1);
            }
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		},
		
		/**
		 * 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) {
Stefanie Lemcke's avatar
Stefanie Lemcke committed
						this.setAttributes(widgetInstance.queryAttributes());
					}
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		
		/**
		 * 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) {
Stefanie Lemcke's avatar
Stefanie Lemcke committed
						this.setConstantAttributes(widgetInstance.queryConstantAttributes());
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.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);
        },
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(_attributeTypes){
			this.initStorage('DB_'+this.name);
			this.setAggregatorAttributeValues(_attributeTypes);
Stefanie Lemcke's avatar
Stefanie Lemcke committed
			this.setAggregatorConstantAttributeValues();
			this.setAggregatorCallbacks();

            this.didFinishSetup();
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		},
		
		/**
		 * 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));
            }
        },

Stefanie Lemcke's avatar
Stefanie Lemcke committed
		/**
		 * 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() {

        },

Stefanie Lemcke's avatar
Stefanie Lemcke committed
		/**
		 * Initializes the provided Callbacks that are only specific to the Aggregator.
		 * Called by aggregatorSetup().
		 * 
		 * @function
		 * @abstract
		 * @protected
		 * @alias setAggregatorCallbacks
		 * @memberof Aggregator#
		 */
		'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;
        },
Stefanie Lemcke's avatar
Stefanie Lemcke committed

		/**
		 * 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);
Stefanie Lemcke's avatar
Stefanie Lemcke committed
				_widget.addSubscriber(subscriber);
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		
		/**
		 * Subscribes to the widgets that are defined in the Widget ID List
Stefanie Lemcke's avatar
Stefanie Lemcke committed
         * used in the initCallback method.
		 * 
		 * @protected
	   	 * @alias initWidgetSubscription
		 * @memberof Aggregator#
		 * @param {String} _widgetId Widget that should be subscribed.
Stefanie Lemcke's avatar
Stefanie Lemcke committed
		 * @returns {?CallbackList}
	     */
		'protected initWidgetSubscription' : function(_widgetId){
Stefanie Lemcke's avatar
Stefanie Lemcke committed
			var calls = null;
			if(Class.isA(String, _widgetId)){
				var widget = this.discoverer.getComponent(_widgetId);
				if (widget){
Stefanie Lemcke's avatar
Stefanie Lemcke committed
					//subscribe to all callbacks
					calls = widget.queryCallbacks();
					this.subscribeTo(widget, calls);
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 {String|Widget|WidgetDescription} _widgetIdOrWidget Widget that should be subscribed.
		 * @param {CallbackList} _callbackList required Callbacks
Stefanie Lemcke's avatar
Stefanie Lemcke committed
	     */
		'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) {
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(_widgetIdOrWidget);
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 {String} _widgetId Widget that should be removed.
Stefanie Lemcke's avatar
Stefanie Lemcke committed
	     */
		'public unsubscribeFrom' : function(_widgetId){
			if(typeof _widgetId == "string"){
				var widget = this.discoverer.getComponent(_widgetId);
				if (widget) {
Stefanie Lemcke's avatar
Stefanie Lemcke committed
					console.log('aggregator unsubscribeFrom: ' + widget.getName());
					widget.removeSubscriber(this.id);
					this.removeWidget(_widgetId);
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, _function){
Stefanie Lemcke's avatar
Stefanie Lemcke committed
			var interpreter = this.discoverer.getComponent(_interpreterId);
			if (Class.isA(Interpreter, interpreter)) {
				interpreter.callInterpreter(this.getAttributeValues(interpreter.getInAttributeTypes()), _function);
Stefanie Lemcke's avatar
Stefanie Lemcke committed
			}
		},
		
		/**
		 * 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) {
Stefanie Lemcke's avatar
Stefanie Lemcke committed
				response = interpreter.getInterpretedData();
				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);
Stefanie Lemcke's avatar
Stefanie Lemcke committed
						if(this.db){
							this.store(theAttribute);
Stefanie Lemcke's avatar
Stefanie Lemcke committed
						}
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 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.
         */
        '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());
                }
Tobias Moebert's avatar
Tobias Moebert committed
        },

		/**
		 * 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());
                }
            }
Tobias Moebert's avatar
Tobias Moebert committed
        },

		/**
		 * 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);
                    }
                });
            });
Stefanie Lemcke's avatar
Stefanie Lemcke committed

	return Aggregator;
});