| // Copyright (c) 2000-2019 Ericsson Telecom AB Telecom AB // |
| // All rights reserved. This program and the accompanying materials are made available under the // |
| // terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at // |
| // https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html // |
| /////////////////////////////////////////////////////////////////////////////////////////////////////// |
| function GuiEditor_SanityChecker_ViewModel(p_model, p_parent) { |
| "use strict"; |
| |
| var v_model = p_model; |
| var v_fileHandler = v_model.getFileHandler(); |
| var v_parent = p_parent; |
| |
| var v_help; |
| var v_request; |
| var v_cache; |
| |
| var v_this = this; |
| |
| ///////////////////// GENERAL VIEWMODEL FUNCTIONS ///////////////////// |
| |
| this.init = function(p_callback) { |
| v_cache = {}; |
| function jsImported(ok, data) { |
| if (!ok) { |
| alert("Some javascript files could not be imported\n" + JSON.stringify(data, null, 4)); |
| } |
| p_callback(true); |
| } |
| |
| new MultipleDirectoryListTask([ |
| v_model.getAppConfig().lastEditedApp + "/ViewModels", |
| "WebApplications/CustomizableApp/ViewModels", |
| v_model.getAppConfig().lastEditedApp + "/Views", |
| "WebApplications/CustomizableApp/Views" |
| ], v_fileHandler).taskOperation(function(ok, resources) { |
| new JsImportTaskList(resources.jsfiles, v_fileHandler, jsImported).taskOperation(); |
| }); |
| }; |
| |
| this.setHelp = function(p_help) { |
| v_help = p_help; |
| }; |
| |
| this.setSetup = function(p_setup) { |
| v_request = p_setup.request.getData(); |
| }; |
| |
| this.addFile = function(fileName) { |
| v_fileHandler.importJsFile(fileName, function(ok, msg) { |
| if (!ok) { |
| alert("Javascript file invalid, import failed: " + fileName + "\n" + msg); |
| } |
| }); |
| }; |
| |
| ///////////////////// INTERFACE FOR VALIDATION AND GETTING HELP ///////////////////// |
| |
| this.isValidView = function(view, connectedViewmodels) { |
| var viewClass = view.getClass(); |
| if (window[viewClass] != undefined) { |
| // check expected interfaces |
| if (window[viewClass].expectsInterface != undefined) { |
| var expectedInterfaces = window[viewClass].expectsInterface(); |
| for (var i = 0; i < connectedViewmodels.length; ++i) { |
| var viewmodelClass = connectedViewmodels[i].getClass(); |
| if (window[viewmodelClass] != undefined && window[viewmodelClass].providesInterface != undefined) { |
| var providedInterface = window[viewmodelClass].providesInterface(); |
| var useful = false; |
| for (var j = 0; j < expectedInterfaces.length; ++j) { |
| var listToSearch = findMandatoryOrOptional(expectedInterfaces[j]);; |
| if (listToSearch != undefined && isSubsetOf(listToSearch, providedInterface)) { |
| expectedInterfaces[j].fulfilled = true; |
| useful = true; |
| } |
| } |
| if (!useful) { |
| return "connected viewmodel " + i + " of class " + viewmodelClass + " does not provide an expected interface"; |
| } |
| } |
| } |
| |
| for (var i = 0; i < expectedInterfaces.length; ++i) { |
| if (!expectedInterfaces[i].fulfilled && expectedInterfaces[i].mandatory != undefined) { |
| return "an interface is not fulfilled: " + expectedInterfaces[i].mandatory; |
| } |
| } |
| } |
| |
| // chack if custom data is valid |
| if (window[viewClass].getCustomDataSchema != undefined && !oldff) { |
| var validate = new Ajv({}).compile(window[viewClass].getCustomDataSchema()); |
| var isValid = validate(view.getCustomData()); |
| if (!isValid) { |
| return "custom data does not match the schema"; |
| } |
| } |
| } |
| |
| return ""; |
| }; |
| |
| this.isValidViewmodel = function(viewmodel) { |
| var viewmodelClass = viewmodel.getClass(); |
| if (window[viewmodelClass] != undefined) { |
| // chack if custom data is valid |
| if (window[viewmodelClass].getCustomDataSchema != undefined && !oldff) { |
| var validate = new Ajv({}).compile(window[viewmodelClass].getCustomDataSchema()); |
| var isValid = validate(viewmodel.getCustomData()); |
| if (!isValid) { |
| return "custom data does not match the schema"; |
| } |
| } |
| // check request connections |
| if (window[viewmodelClass].expectsConnection != undefined) { |
| var expectedConnections = window[viewmodelClass].expectsConnection(); |
| var dataConnections = viewmodel.getDataConnections(); |
| var selectionConnections = viewmodel.getSelectionConnections(); |
| var connectionsOk = checkRequestToViewmodelConnections(dataConnections, selectionConnections, expectedConnections); |
| if (connectionsOk != "") { |
| return connectionsOk; |
| } |
| } |
| } |
| |
| return ""; |
| }; |
| |
| this.getHelp = function(className) { |
| if (window[className] != undefined && window[className].getHelp != undefined) { |
| return window[className].getHelp(); |
| } else { |
| return "No help available for " + className; |
| } |
| }; |
| |
| this.getCustomDataSchema = function(className) { |
| if (window[className] != undefined && window[className].getCustomDataSchema != undefined) { |
| return window[className].getCustomDataSchema(); |
| } else { |
| return undefined; |
| } |
| }; |
| |
| ///////////////////// PRIVATE FUNCTIONS ///////////////////// |
| |
| function isSubsetOf(subset, set) { |
| var found = true; |
| for (var j = 0; j < subset.length && found; ++j) { |
| found = set.indexOf(subset[j]) != -1; |
| } |
| return found; |
| } |
| |
| function findMandatoryOrOptional(expectedInterfaces) { |
| if (expectedInterfaces.mandatory != undefined) { |
| return expectedInterfaces.mandatory; |
| } else if (expectedInterfaces.optional != undefined) { |
| return expectedInterfaces.optional; |
| } else { |
| return undefined; |
| } |
| } |
| |
| function checkRequestToViewmodelConnections(dataConnections, selectionConnections, expectedConnections) { |
| var expectedDataConnections = expectedConnections.dataConnections; |
| if (expectedDataConnections != undefined) { |
| if (expectedDataConnections.length == 0 && dataConnections.length > 0) { |
| return "no data connections expected"; |
| } |
| |
| for (var i = 0; i < dataConnections.length; ++i) { |
| |
| var expectedConnectionIndex = Math.min(i, expectedDataConnections.length - 1); |
| var expectedConnection = expectedDataConnections[expectedConnectionIndex]; |
| if (i >= expectedDataConnections.length && expectedConnectionIndex == expectedDataConnections.length - 1 && expectedConnection.multiple !== true) { |
| return "more data connections detected than expected"; |
| } |
| |
| var result = checkRequestToViewmodelConnection(dataConnections, selectionConnections, expectedConnection, dataConnections, i, "data connection"); |
| if (result != "") { |
| return result; |
| } |
| } |
| |
| if (expectedDataConnections[dataConnections.length] != undefined && (expectedDataConnections[dataConnections.length].optional == undefined || expectedDataConnections[dataConnections.length].optional == false)) { |
| return "missing non optional data connection"; |
| } |
| } |
| |
| var expectedSelectionConnections = expectedConnections.selectionConnections; |
| if (expectedSelectionConnections != undefined) { |
| if (expectedSelectionConnections.length == 0 && selectionConnections.length > 0) { |
| return "no selection connections expected"; |
| } |
| |
| for (var i = 0; i < selectionConnections.length; ++i) { |
| |
| var expectedConnectionIndex = Math.min(i, expectedSelectionConnections.length - 1); |
| var expectedConnection = expectedSelectionConnections[expectedConnectionIndex]; |
| if (i >= expectedDataConnections.length && expectedConnectionIndex == expectedSelectionConnections.length - 1 && expectedConnection.multiple !== true) { |
| return "more selection connections detected than expected"; |
| } |
| |
| var result = checkRequestToViewmodelConnection(dataConnections, selectionConnections, expectedConnection, selectionConnections, i, "selection connection"); |
| if (result != "") { |
| return result; |
| } |
| |
| if (expectedSelectionConnections[selectionConnections.length] != undefined && (expectedSelectionConnections[selectionConnections.length].optional == undefined || expectedSelectionConnections[selectionConnections.length].optional == false)) { |
| return "missing non optional selection connection"; |
| } |
| } |
| } |
| |
| return ""; |
| } |
| |
| function checkRequestToViewmodelConnection(dataConnections, selectionConnections, expectedConnection, connections, indexOfConnection, connectionType) { |
| if (expectedConnection.childOfDataConnection != undefined) { |
| if (dataConnections[expectedConnection.childOfDataConnection] == undefined || !hasPrefix(connections[indexOfConnection], dataConnections[expectedConnection.childOfDataConnection])) { |
| return connectionType + " " + indexOfConnection + " is not a child of data connection " + expectedConnection.childOfDataConnection; |
| } |
| } |
| |
| var request = getRequestFromPath(connections[indexOfConnection]); |
| |
| if (expectedConnection.element != undefined && expectedConnection.element != request.getData.element) { |
| return "wrong element found for " + connectionType + " " + indexOfConnection + ", expected " + expectedConnection.element; |
| } |
| if (expectedConnection.source != undefined && expectedConnection.source != request.getData.source) { |
| return "wrong source found for " + connectionType + " " + indexOfConnection + ", expected " + expectedConnection.source; |
| } |
| if (expectedConnection.filter == true && request.getData.filter == undefined) { |
| return "expected a filter for " + connectionType + " " + indexOfConnection; |
| } |
| |
| if (expectedConnection.valueType != undefined) { |
| var helpDataElement = getHelpFromRequest(request).dataElement; |
| if (helpDataElement != undefined && expectedConnection.valueType.indexOf(helpDataElement.valueType) == -1) { |
| return "wrong value type for " + connectionType + " " + indexOfConnection + ", expected " + expectedConnection.valueType; |
| } |
| } |
| |
| if ( |
| expectedConnection.dataElementOfDataConnection != undefined && ( |
| dataConnections[expectedConnection.dataElementOfDataConnection] == undefined || |
| getHelpFromRequest(request).index != getHelpFromRequest(getRequestFromPath(dataConnections[expectedConnection.dataElementOfDataConnection])).index |
| ) |
| ) { |
| return connectionType + " " + indexOfConnection + " is not the same element as data connection " + expectedConnection.dataElementOfDataConnection; |
| } |
| |
| return ""; |
| |
| } |
| |
| function getRequestFromPath(p_path) { |
| var request = v_request[p_path[0]]; |
| for (var i = 1; i < p_path.length; ++i) { |
| request = request.getData.children[p_path[i]]; |
| } |
| return request; |
| } |
| |
| function hasPrefix(path, prefix) { |
| for (var i = 0; i < prefix.length; ++i) { |
| if (path[i] == undefined || path[i] != prefix[i]) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| function getHelpFromRequest(request) { |
| var requestHash = getRequestHash(request); |
| if (v_cache[requestHash] != undefined) { |
| return v_cache[requestHash]; |
| } |
| |
| var result; |
| var bestIndex; |
| var bestScore = 0; |
| var source = request.getData.source; |
| if (v_help) |
| for (var i = 0; i < v_help.sources.length; ++i) { |
| if (v_help.sources[i].source == source) { |
| for (var j = 0; j < v_help.sources[i].dataElements.length; ++j) { |
| var score = isCorrectElementForRequest(v_help.sources[i].dataElements[j].dataElement, request); |
| if (score > bestScore) { |
| result = { |
| "index": j, |
| "dataElement": v_help.sources[i].dataElements[j].dataElement |
| }; |
| bestScore = score; |
| } |
| } |
| } |
| } |
| |
| if (bestScore == 0) { |
| result = { |
| "index": -1 |
| } |
| } |
| |
| v_cache[requestHash] = result; |
| return result; |
| } |
| |
| function isCorrectElementForRequest(dataElement, request) { |
| if (dataElement.name != request.getData.element) { |
| return 0; |
| } |
| |
| var score = 1; |
| var params = getParamTypes(request); |
| if (dataElement.parameters != undefined) { |
| for (var i = 0; i < dataElement.parameters.length; ++i) { |
| if (dataElement.parameters[i].typeDescriptor != undefined && dataElement.parameters[i].typeDescriptor.reference != undefined && dataElement.parameters[i].typeDescriptor.reference.typeName != undefined) { |
| if (params.indexOf(dataElement.parameters[i].typeDescriptor.reference.typeName) != -1) { |
| ++score; |
| } |
| } |
| } |
| } |
| |
| return score; |
| } |
| |
| function getRequestHash(request) { |
| var hash = request.getData.source + "." + request.getData.element; |
| var params = getParamTypes(request); |
| for (var i = 0; i < params.length; ++i) { |
| hash += "." + params[i]; |
| } |
| |
| return hash; |
| } |
| |
| function getParamTypes(request) { |
| var params = []; |
| if (request.getData.params != undefined) { |
| for (var i = 0; i < request.getData.params.length; ++i) { |
| params.push(request.getData.params[i].paramName); |
| } |
| } |
| params.sort(); |
| return params; |
| } |
| } |
| //# sourceURL=GuiEditor\ViewModels\ViewModel_SanityChecker.js |