Skip to content
Snippets Groups Projects
discoverer.js 22.1 KiB
Newer Older
define(['attributeList', 'attribute', 'translation', 'parameter', 'parameterList', 'widget', 'interpreter', 'aggregator',  'interpretation' ],
	function(AttributeList, Attribute, Translation, Parameter, ParameterList, Widget, Interpreter, Aggregator, Interpretation) {
		return (function() {
			/**
			 * Constructor: All known components given in the associated functions will be registered as startup.
			 *
			 * @classdesc The Discoverer handles requests for components and attributes.
			function Discoverer(widgets, interpreters, translations) {
				/**
				 * List of available Widgets.
				 *
				 * @type {Array}
				 * @private
				 */
				this._widgets = [];

				/**
				 * List of unregistered Widgets
				 *
				 * @type (Array)
				 * @private
				 */
				this._unregisteredWidgets = widgets;


				/**
				 * List of available Aggregators.
				 *
				 * @type {Array}
				 * @private
				 */
				this._aggregators = [];

				/**
				 * List of available Interpreter.
				 *
				 * @type {Object}
				 * @private
				 */
				this._interpreters = [];

				/**
				 * List of unregistered Interpreters
				 *
				 * @type (Array)
				 * @private
				 */
				this._unregisteredInterpreters = interpreters;

				/**
				 * List of translations or synonymous attributes, respectively.
				 *
				 * @type {Array}
				 * @private
				 */
Helena Jank's avatar
Helena Jank committed
				// build translations from input array
                for (var i in translations) {
Helena Jank's avatar
Helena Jank committed
					// get translation (an array) from array of translations
                    var translationArray = translations[i];
Helena Jank's avatar
Helena Jank committed
					// check for correct cardinality
                    if (translationArray.length != 2)
                        throw new Error("Translations must consist of exactly 2 attributes!");
Helena Jank's avatar
Helena Jank committed
					// check for correct number of attribute building blocks
					for (var j in translationArray) {
                        if (translationArray[j].length > 3 || translationArray[j].length < 2)
                            throw new Error("Please provide a name, type and (optional) list of parameters!");
Helena Jank's avatar
Helena Jank committed
					// build attributes from arrays containing name, type (and parameters)
                    var firstAttribute = this.buildAttribute(
                        translationArray[0][0],
                        translationArray[0][1],
                        translationArray[0][2],
                        false
                    );
                    var secondAttribute = this.buildAttribute(
                        translationArray[1][0],
                        translationArray[1][1],
                        translationArray[1][2],
                        false
                    );
Helena Jank's avatar
Helena Jank committed
					// add built attributes to translations
                    this._translations.push(new Translation(firstAttribute,secondAttribute));
                }
				return this;
Stefanie Lemcke's avatar
Stefanie Lemcke committed
			}

			/**
			 * Registers the specified component.
			 *
			 * @param {Widget|Aggregator|Interpreter} component the component that should be registered
			 */
			Discoverer.prototype.registerNewComponent = function(component) {
				if (component instanceof Aggregator && this.getAggregator(component.getId()) == null) this._aggregators.push(component);
				if (component instanceof Widget && !(component instanceof Aggregator) && this.getWidget(component.getId()) == null) this._widgets.push(component);
				if (component instanceof Interpreter && this.getInterpreter(component.getId()) == null) this._interpreters.push(component);
			};

			/**
			 * Deletes a component from the Discoverer.
			 *
			 * @param {string} componentId id of the component that should be registered
			 */
			Discoverer.prototype.unregisterComponent = function(componentId) {
				for (var wi in this._widgets) {
					var theWidget = this._widgets[wi];
					if (componentId == theWidget.getId()) this._widgets.splice(wi, 1);
				}
				for (var ii in this._interpreters) {
					var theInterpreter = this._interpreters[ii];
					if (componentId == theInterpreter.getId()) this._interpreters.splice(ii, 1);
				}
				for (var ai in this._aggregators) {
					var theAggregator= this._aggregators[ai];
					if (componentId == theAggregator.getId()) this._aggregators.splice(ai, 1);
				}
			};

			/**
			 * Returns the widget for the specified id.
			 *
			 * @param {string} widgetId id of the component that should be returned
			 * @returns {?Widget}
			 */
			Discoverer.prototype.getWidget = function(widgetId) {
				for (var index in this._widgets) {
					var theWidget = this._widgets[index];
					if (theWidget.getId() == widgetId) return theWidget;
				}
				return null;
			};

			/**
			 * Returns the aggregator for the specified id.
			 *
			 * @param {string} aggregatorId id of the component that should be returned
			 * @returns {?Aggregator}
			 */
			Discoverer.prototype.getAggregator = function(aggregatorId) {
				for (var index in this._aggregators) {
					var theAggregator = this._aggregators[index];
					if (theAggregator.getId() == aggregatorId) return theAggregator;
				}
				return null;
			};

			/**
			 * Returns the interpreter for the specified id.
			 *
			 * @param {string} interpreterId id of the component that should be returned
			 * @returns {Interpreter}
			 */
			Discoverer.prototype.getInterpreter = function(interpreterId) {
				for (var index in this._interpreters) {
					var theInterpreter = this._interpreters[index];
					if (theInterpreter.getId() == interpreterId) return theInterpreter;
				}
				return null;
			};

			/**
			 * Returns all registered components (widget, aggregator and interpreter).
			 *
			 * @param {Array} componentTypes Component types to get descriptions for. Defaults to Widget, Interpreter and Aggregator.
			 * @returns {Array}
			 */
			Discoverer.prototype.getComponents = function(componentTypes) {
				if (typeof componentTypes == "undefined") componentTypes = [Widget, Interpreter, Aggregator];
				var response = [];
				if (jQuery.inArray(Widget, componentTypes) != -1) response = response.concat(this._widgets);
				if (jQuery.inArray(Aggregator, componentTypes) != -1) response = response.concat(this._aggregators);
				if (jQuery.inArray(Interpreter, componentTypes) != -1) response = response.concat(this._interpreters);
				return response;
			};

			/**
			 * Returns the instance (widget, aggregator or interpreter) for the specified id.
			 *
			 * @param {string} componentId id of the component that should be returned
			 * @returns {?(Widget|Aggregator|Interpreter)}
			 */
			Discoverer.prototype.getComponent = function(componentId) {
				var theWidget = this.getWidget(componentId);
				if (theWidget) {
					return theWidget;
				}
				var theAggregator = this.getAggregator(componentId);
				if (theAggregator) {
					return theAggregator;
				}
				var theInterpreter = this.getInterpreter(componentId);
				if (theInterpreter) {
					return theInterpreter;
				}
				return null;
			};

			/**
			 * Returns all components that have the specified attribute as
			 * outAttribute. It can be chosen between the verification of
			 * all attributes or at least one attribute.
			 *
			 * @param {AttributeList|Array} attributeListOrArray list of searched attributes
			 * @param {Boolean} all choise of the verification mode
			 * @param {Array} componentTypes Components types to search for
			 * @returns {Array}
			 */
			Discoverer.prototype.getComponentsByAttributes = function(attributeListOrArray, all, componentTypes) {
				var componentList = [];
				var list = [];
				if (typeof componentTypes == "undefined") componentTypes = [Widget, Interpreter, Aggregator];
				if (attributeListOrArray instanceof Array) {
					list = attributeListOrArray;
				} else if (attributeListOrArray.constructor === AttributeList) {
					list = attributeListOrArray.getItems();
				}
				if (typeof list != "undefined") {
					var components = this.getComponents(componentTypes);
					for (var i in components) {
						var theComponent = components[i];
						if(all && this._containsAllAttributes(theComponent, list)) {
							componentList.push(theComponent);
						} else if(!all && this._containsAtLeastOneAttribute(theComponent, list)) {
							componentList.push(theComponent);
Stefanie Lemcke's avatar
Stefanie Lemcke committed
					}
				}
				return componentList;
			};

			/**
			 * Returns an array of all translations known to the discoverer.
			 *
			 * @returns {Array}
			 */
			Discoverer.prototype.getTranslations = function() {
				return this._translations;
			};

			/**
			 * Builds a new attribute from given name, type and parameters,
			 * adding known translations to its synonyms.
			 *
			 * @param name
			 * @param type
             * @param {Boolean} [withSynonyms=true]
			Discoverer.prototype.buildAttribute = function(name, type, parameterList, withSynonyms) {
				if (typeof withSynonyms == 'undefined') withSynonyms = true;
				if (typeof parameterList == 'undefined') parameterList = [];

                if (typeof name != 'string' || typeof type != 'string')
                    throw new Error("Parameters name and type must be of type String!");

                var newAttribute = new Attribute(true).withName(name).withType(type);
				for (var i = 0; i < parameterList.length; i++) {
					var param = parameterList[i];
					var value = param[1];
					var key = param[0];
					if (typeof key != 'undefined' && typeof value != 'undefined') newAttribute = newAttribute.withParameter(new Parameter().withKey(key).withValue(value));
                    for (var index in this._translations) {
                        var translation = this._translations[index];
						newAttribute = translation.translate(newAttribute);
			/***********************************************************************
			 * Helper *
			 **********************************************************************/
			/**
			 * Helper: Verifies whether a component description contains all searched attributes.
			 *
			 * @private
			 * @param {Widget|Interpreter|Aggregator} component description of a component
			 * @param {Array} list searched attributes
			 * @returns {boolean}
			 */
			Discoverer.prototype._containsAllAttributes = function(component, list) {
				for (var j in list) {
					var attribute = list[j];
					if (!component.doesSatisfyTypeOf(attribute)) {
						return false;
					}
Stefanie Lemcke's avatar
Stefanie Lemcke committed
				}
				return true;
			};

			/**
			 * Helper: Verifies whether a component description contains at least on searched attributes.
			 *
			 * @private
			 * @param {Widget|Interpreter|Aggregator} component description of a component
			 * @param {Array} list searched attributes
			 * @returns {boolean}
			 */
			Discoverer.prototype._containsAtLeastOneAttribute = function(component, list) {
				for (var j in list) {
					var attribute = list[j];
					if (component.doesSatisfyTypeOf(attribute)) {
						return true;
					}
Stefanie Lemcke's avatar
Stefanie Lemcke committed
				}
				return false;
			};
Stefanie Lemcke's avatar
Stefanie Lemcke committed

			/**
			 * Searches for components that can satisfy the requested attributes. Searches recursively through all
			 * registered and unregistered components and initiates them.
			 *
			 * @param {String} aggregatorId The aggregator's ID
			 * @param {AttributeList} 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).
mirueger's avatar
mirueger committed
			Discoverer.prototype.getComponentsForUnsatisfiedAttributes = function(aggregatorId, unsatisfiedAttributes, all, componentTypes){
				// the discoverer gets a list of attributes to satisfy
				console.log('Discoverer: I will look for components that satisfy the following Attributes: '+unsatisfiedAttributes.getItems()+'.' );
				// look at all the already registered components
				this._getRegisteredComponentsForUnsatisfiedAttributes(aggregatorId, unsatisfiedAttributes, all, componentTypes);
				// look at all unregistered components
				this._getUnregisteredComponentsForUnsatisfiedAttributes(aggregatorId, unsatisfiedAttributes);
			};
			/**
			 * Searches for registered components that satisfy the requested attributes.
			 *
			 * @param {String} aggregatorId The aggregator's ID
			 * @param {AttributeList} 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
			 */
			Discoverer.prototype._getRegisteredComponentsForUnsatisfiedAttributes = function(aggregatorId, unsatisfiedAttributes, all, componentTypes) {
				var theAggregator = this.getAggregator(aggregatorId);
				console.log("Discoverer: Let's look at my registered components.");

				var relevantComponents = this.getComponentsByAttributes(unsatisfiedAttributes, all, componentTypes);
				console.log("Discoverer: I found " + relevantComponents.length + " registered component(s) that might satisfy the requested attributes.");

				//iterate over the found components
				for(var index in relevantComponents) {
					var theComponent = relevantComponents[index];
					console.log("Discoverer: Let's look at component "+theComponent.getName()+".");

					// if the component was added before, ignore it
					if (!theAggregator._hasComponent(theComponent.getId())) {
						// if component is a widget and it wasn't added before, subscribe to its callbacks
						if (theComponent instanceof Widget) {
							theAggregator.addWidgetSubscription(theComponent);
							console.log("Discoverer: I found "+theComponent.name+" and the Aggregator did subscribe to it.");
							this._removeAttributesSatisfiedByWidget(aggregatorId, theComponent, unsatisfiedAttributes);
						} else if (theComponent instanceof Interpreter) { // if the component is an interpreter and all its in attributes can be satisfied, add the interpreter
							console.log("Discoverer: It's an Interpreter.");

							if (this._checkInterpreterInAttributes(aggregatorId, theComponent)) {
								// remove satisfied attributes
								this._removeAttributesSatisfiedByInterpreter(aggregatorId, theComponent, unsatisfiedAttributes);
								console.log("Discoverer: I found a registered Interpreter but I couldn't satisfy the required attributes.");
								for (var j in theComponent.getInAttributes().getItems()) {
									console.log("Discoverer: It is missing " + theComponent.getInAttributes().getItems()[j] + ".");
						} else {
							console.log("Discoverer: It seems that the component was added before.");
			};

			/**
			 * Searches for unregistered components that satisfy the requested attributes.
			 *
			 * @param {String} aggregatorId The aggregator's ID
			 * @param {AttributeList} unsatisfiedAttributes A list of attributes that components should be searched for.
			 * @private
			 */
			Discoverer.prototype._getUnregisteredComponentsForUnsatisfiedAttributes = function(aggregatorId, unsatisfiedAttributes) {
				var theAggregator = this.getAggregator(aggregatorId);
				console.log("Discoverer: Let's look at the unregistered components.");

				//check all Widget's outAttributes
				var foundWidget = false;
				for(var widgetIndex in this._unregisteredWidgets){
					var theWidget = this._unregisteredWidgets[widgetIndex];
					for(var unsatisfiedAttributeIndex in unsatisfiedAttributes.getItems()){
						var theUnsatisfiedAttribute = unsatisfiedAttributes.getItems()[unsatisfiedAttributeIndex];
						//if a Widget can satisfy the Attribute, register it and subscribe the Aggregator

						//create temporary OutAttributeList
						var tempWidgetOutList = AttributeList.fromAttributeDescription(this, theWidget.inOut.out);
						for(var tempWidgetOutListIndex in tempWidgetOutList.getItems()) {
							if (theUnsatisfiedAttribute.equalsTypeOf(tempWidgetOutList.getItems[tempWidgetOutListIndex])) {
								console.log("Discoverer: I have found an unregistered "+theWidget.name+".");
								var newWidget = new theWidget(this, tempWidgetOutList);
								theAggregator.addWidgetSubscription(newWidget);
								console.log("Discoverer: I registered "+theWidget.name+" and the Aggregator subscribed to it.");
								// remove satisfied attributes
								this._removeAttributesSatisfiedByWidget(aggregatorId, newWidget, unsatisfiedAttributes);
				if (!foundWidget) {
					//check all interpreters' outAttributes
					for (var index in this._unregisteredInterpreters) {
						var theInterpreter = this._unregisteredInterpreters[index];
						for (var unsatisfiedAttributeIndex in unsatisfiedAttributes.getItems()) {
							var theUnsatisfiedAttribute = unsatisfiedAttributes.getItems()[unsatisfiedAttributeIndex];
							//create temporary outAttributeList
							var tempOutList = AttributeList.fromAttributeDescription(this, theInterpreter.inOut.out);
							//create temporary inAttributeList
							var tempInList = AttributeList.fromAttributeDescription(this, theInterpreter.inOut.in);
							for (var tempOutAttributeIndex in tempOutList.getItems()) {
								if (theUnsatisfiedAttribute.equalsTypeOf(tempOutList.getItems()[tempOutAttributeIndex])) {
									console.log("Discoverer: I have found an unregistered "+theInterpreter.name+" that might satisfy the requested Attribute.");

									//if an interpreter can satisfy the Attribute, check if the inAttributes are satisfied
									if (this._checkInterpreterInAttributes(aggregatorId, theInterpreter)) {
										var newInterpreter = new theInterpreter(this, tempInList, tempOutList);
										//theAggregator.addWidgetSubscription(newInterpreter);
										console.log("Discoverer: I registered the Interpreter \"" + theInterpreter.name + "\" .");
										// remove satisfied attributes
										this._removeAttributesSatisfiedByInterpreter(aggregatorId, newInterpreter, unsatisfiedAttributes);
									} else {
										console.log("Discoverer: I found an unregistered Interpreter but I couldn't satisfy the required Attributes.");
						}
					}
				}
			};

			/**
			 *
			 * @param aggregatorId
			 * @param theInterpreter
			 * @returns {boolean}
			 * @private
			 */
			Discoverer.prototype._checkInterpreterInAttributes = function(aggregatorId, theInterpreter) {
				var theAggregator = this.getComponent(aggregatorId);
				var canSatisfyInAttributes = true;
				var attributes;
				if (theInterpreter instanceof Interpreter) {
					attributes = theInterpreter.getInAttributes().getItems();
				} else {
					attributes = AttributeList.fromAttributeDescription(this, theInterpreter.inOut.in).getItems();
				}

				for (var attributeIdentifier in attributes) {
					// get the attribute
					var theAttribute = attributes[attributeIdentifier];
					console.log("Discoverer: The Interpreter needs the Attribute: "+theAttribute.toString(true)+".");
					// if required attribute is not already satisfied by the aggregator search for components that do
					if (!theAggregator.doesSatisfyTypeOf(theAttribute)) {
						console.log("Discoverer: The Aggregator doesn't satisfy "+theAttribute.toString(true)+", but I will search for components that do.");
						var newAttributeList = new AttributeList();
						newAttributeList.put(theAttribute);
						this.getComponentsForUnsatisfiedAttributes(aggregatorId, newAttributeList, false, [Widget, Interpreter]);
						// if the attribute still can't be satisfied drop the interpreter
						if (!theAggregator.doesSatisfyTypeOf(theAttribute)) {
							console.log("Discoverer: I couldn't find a component to satisfy "+theAttribute.toString(true)+". Dropping interpreter.");
							canSatisfyInAttributes = false;
							break;
						}
					} else {
						console.log("Discoverer: It seems that the Aggregator already satisfies the Attribute "+theAttribute.toString(true)+". Will move on.");
					}
				}

				return canSatisfyInAttributes;
			};

			/**
			 *
			 * @param aggregatorId
			 * @param theWidget
			 * @param unsatisfiedAttributes
			 * @private
			 */
			Discoverer.prototype._removeAttributesSatisfiedByWidget = function(aggregatorId, theWidget, unsatisfiedAttributes) {
				var theAggregator = this.getAggregator(aggregatorId);

				var attributes = theWidget.getOutAttributes().getItems();
				for (var attributeIndex in attributes) {
					var theAttribute = attributes[attributeIndex];
					// add the attribute type to the aggregator's list of handled attribute types
					if (!theAggregator.getOutAttributes().containsTypeOf(theAttribute)) theAggregator.addOutAttribute(theAttribute);
					console.log("Discoverer: The Aggregator can now satisfy attribute "+theAttribute.toString(true)+" with the help of "+theWidget.getName()+".");
					unsatisfiedAttributes.removeAttributeWithTypeOf(theAttribute);
				}
			};
			/**
			 *
			 * @param aggregatorId
			 * @param theInterpreter
			 * @param unsatisfiedAttributes
			 * @private
			 */
			Discoverer.prototype._removeAttributesSatisfiedByInterpreter = function(aggregatorId, theInterpreter, unsatisfiedAttributes) {
				var theAggregator = this.getAggregator(aggregatorId);

				var attributes = theInterpreter.getOutAttributes().getItems();
				for (var attributeIndex in attributes) {
					var theAttribute = attributes[attributeIndex];
					// add the attribute type to the aggregator's list of handled attribute types
					for (var unsatisfiedAttributeIndex in unsatisfiedAttributes.getItems()) {
						var theUnsatisfiedAttribute = unsatisfiedAttributes.getItems()[unsatisfiedAttributeIndex];
						if (theUnsatisfiedAttribute.equalsTypeOf(theAttribute)) {
							if (!theAggregator.getOutAttributes().containsTypeOf(theAttribute)) theAggregator.addOutAttribute(theAttribute);
							console.log("Aggregator: The Aggregator can now satisfy Attribute "+theAttribute.toString(true)+" with the help of "+theInterpreter.getName()+".");
							theAggregator._interpretations.push(new Interpretation(theInterpreter.getId(), theInterpreter.getInAttributes(), new AttributeList().withItems([theUnsatisfiedAttribute])));
					unsatisfiedAttributes.removeAttributeWithTypeOf(theAttribute, true);
Stefanie Lemcke's avatar
Stefanie Lemcke committed

			return Discoverer;
		})();
	}
);