UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

470 lines (427 loc) 13.6 kB
/*! * OpenUI5 * (c) Copyright 2009-2021 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ /** * XML-based DataBinding * * @namespace * @name sap.ui.model.xml * @public */ // Provides the XML object based model implementation sap.ui.define([ 'sap/ui/model/ClientModel', 'sap/ui/model/Context', './XMLListBinding', './XMLPropertyBinding', './XMLTreeBinding', "sap/ui/util/XMLHelper", "sap/base/Log", "sap/base/util/each" ], function( ClientModel, Context, XMLListBinding, XMLPropertyBinding, XMLTreeBinding, XMLHelper, Log, each ) { "use strict"; /** * Constructor for a new XMLModel. * * @class * Model implementation for XML format * * @extends sap.ui.model.ClientModel * * @author SAP SE * @version 1.87.1 * * @param {object} oData either the URL where to load the XML from or an XML * @public * @alias sap.ui.model.xml.XMLModel */ var XMLModel = ClientModel.extend("sap.ui.model.xml.XMLModel", /** @lends sap.ui.model.xml.XMLModel.prototype */ { constructor : function (oData) { ClientModel.apply(this, arguments); this.oNameSpaces = null; if (oData && oData.documentElement) { this.setData(oData); } }, metadata : { publicMethods : ["setXML", "getXML", "setNameSpace"] } }); /** * Sets the specified XML formatted string text to the model * * @param {string} sXMLText the XML data as string * @public */ XMLModel.prototype.setXML = function(sXMLText){ var oXMLDocument = XMLHelper.parse(sXMLText); if (oXMLDocument.parseError.errorCode != 0) { var oParseError = oXMLDocument.parseError; Log.fatal("The following problem occurred: XML parse Error for " + oParseError.url + " code: " + oParseError.errorCode + " reason: " + oParseError.reason + " src: " + oParseError.srcText + " line: " + oParseError.line + " linepos: " + oParseError.linepos + " filepos: " + oParseError.filepos); this.fireParseError({url : oParseError.url, errorCode : oParseError.errorCode, reason : oParseError.reason, srcText : oParseError.srcText, line : oParseError.line, linepos : oParseError.linepos, filepos : oParseError.filepos}); } this.setData(oXMLDocument); }; /** * Serializes the current XML data of the model into a string. * * @return the XML document serialized as string * @public */ XMLModel.prototype.getXML = function(){ return XMLHelper.serialize(this.oData); }; /** * Sets the provided XML encoded data object to the model * * @param {object} oData the data to set to the model * @public */ XMLModel.prototype.setData = function(oData){ this.oData = oData; this.checkUpdate(); }; /** * Load XML-encoded data from the server using a GET HTTP request and store the resulting XML data in the model. * Note: Due to browser security restrictions, most "Ajax" requests are subject to the same origin policy, * the request can not successfully retrieve data from a different domain, subdomain, or protocol. * * @param {string} sURL A string containing the URL to which the request is sent * @param {object | string} [oParameters] A map of parameters or a single parameter string that is sent to the server with the request * @param {boolean} [bAsync=true] By default, all requests are sent asynchronous. * <b>Do not use <code>bAsync=false</code></b> because synchronous requests may temporarily lock * the browser, disabling any actions while the request is active. Cross-domain requests do not * support synchronous operation. * @param {string} [sType=GET] HTTP method of request * @param {string} [bCache=false] Force no caching if false * @param {object} [mHeaders] An object of additional header key/value pairs to send along with the request * * @public */ XMLModel.prototype.loadData = function(sURL, oParameters, bAsync, sType, bCache, mHeaders){ var that = this; bAsync = (bAsync !== false); sType = sType || "GET"; bCache = bCache === undefined ? this.bCache : bCache; this.fireRequestSent({url : sURL, type : sType, async : bAsync, headers: mHeaders, info : "cache=" + bCache, infoObject: {cache: bCache}}); this._ajax({ url: sURL, async: bAsync, cache: bCache, dataType: 'xml', data: oParameters, headers: mHeaders, type: sType, success: function(oData) { if (!oData) { Log.fatal("The following problem occurred: No data was retrieved by service: " + sURL); } that.setData(oData); that.fireRequestCompleted({url : sURL, type : sType, async : bAsync, headers: mHeaders, info : "cache=" + bCache, infoObject: {cache: bCache}, success: true}); }, error: function(XMLHttpRequest, textStatus, errorThrown){ var oError = { message : textStatus, statusCode : XMLHttpRequest.status, statusText : XMLHttpRequest.statusText, responseText : XMLHttpRequest.responseText}; Log.fatal("The following problem occurred: " + textStatus, XMLHttpRequest.responseText + "," + XMLHttpRequest.status + "," + XMLHttpRequest.statusText); that.fireRequestCompleted({url : sURL, type : sType, async : bAsync, headers: mHeaders, info : "cache=" + bCache, infoObject: {cache: bCache}, success: false, errorobject: oError}); that.fireRequestFailed(oError); } }); }; /** * Sets an XML namespace to use in the binding path * * @param {string} sNameSpace the namespace URI * @param {string} [sPrefix=null] the prefix for the namespace (optional) * @public */ XMLModel.prototype.setNameSpace = function(sNameSpace, sPrefix){ if (!sPrefix) { sPrefix = ""; } if (!this.oNameSpaces) { this.oNameSpaces = {}; } this.oNameSpaces[sPrefix] = sNameSpace; }; /** * @see sap.ui.model.Model.prototype.bindProperty */ XMLModel.prototype.bindProperty = function(sPath, oContext, mParameters) { var oBinding = new XMLPropertyBinding(this, sPath, oContext, mParameters); return oBinding; }; /** * @see sap.ui.model.Model.prototype.bindList */ XMLModel.prototype.bindList = function(sPath, oContext, aSorters, aFilters, mParameters) { var oBinding = new XMLListBinding(this, sPath, oContext, aSorters, aFilters, mParameters); return oBinding; }; /** * @see sap.ui.model.Model.prototype.bindTree */ XMLModel.prototype.bindTree = function(sPath, oContext, aFilters, mParameters, aSorters) { var oBinding = new XMLTreeBinding(this, sPath, oContext, aFilters, mParameters, aSorters); return oBinding; }; /** * Sets a new value for the given property <code>sPropertyName</code> in the model. * If the model value changed all interested parties are informed. * * @param {string} sPath path of the property to set * @param {any} oValue value to set the property to * @param {object} [oContext=null] the context which will be used to set the property * @param {boolean} [bAsyncUpdate] whether to update other bindings dependent on this property asynchronously * @return {boolean} true if the value was set correctly and false if errors occurred like the entry was not found. * @public */ XMLModel.prototype.setProperty = function(sPath, oValue, oContext, bAsyncUpdate) { var sObjectPath = sPath.substring(0, sPath.lastIndexOf("/") + 1), sProperty = sPath.substr(sPath.lastIndexOf("/") + 1); // check if path / context is valid if (!this.resolve(sPath, oContext)) { return false; } if (!this.oData.documentElement) { Log.warning("Trying to set property " + sPath + ", but no document exists."); return false; } var oObject; if (sProperty.indexOf("@") == 0) { oObject = this._getObject(sObjectPath, oContext); if (oObject[0]) { oObject[0].setAttribute(sProperty.substr(1), oValue); this.checkUpdate(false, bAsyncUpdate); return true; } } else { oObject = this._getObject(sPath, oContext); if (oObject[0]) { oObject[0].textContent = oValue; this.checkUpdate(false, bAsyncUpdate); return true; } } return false; }; /** * Returns the value for the property with the given <code>sPropertyName</code> * * @param {string} sPath the path to the property * @param {object} [oContext=null] the context which will be used to retrieve the property * @type any * @return the value of the property * @public */ XMLModel.prototype.getProperty = function(sPath, oContext) { var oResult = this._getObject(sPath, oContext); if (oResult && typeof oResult != "string") { oResult = oResult[0] ? oResult[0].textContent : ""; // TODO is this right? shouldn't we return the object?! } return oResult; }; /** * Returns the object for the given <code>path</code> * * @param {string} sPath the path to the object * @param {object} [oContext=null] the context which will be used to retrieve the object * @type any * @return the object * @public */ XMLModel.prototype.getObject = function(sPath, oContext) { var oObject = this._getObject(sPath, oContext); if (Array.isArray(oObject)) { oObject = oObject[0]; } return oObject; }; /** * @param {string} sPath * @param {object} oContext * @returns {any} the node of the specified path/context */ XMLModel.prototype._getObject = function (sPath, oContext) { var oRootNode = this.oData.documentElement; if (!oRootNode) { return null; } var oNode = this.isLegacySyntax() ? [oRootNode] : []; if (oContext instanceof Context) { oNode = this._getObject(oContext.getPath()); } else if (oContext) { oNode = [oContext]; } if (!sPath) { return oNode; } var aParts = sPath.split("/"), sPart, iIndex = 0; if (!aParts[0]) { // absolute path starting with slash oNode = oRootNode; iIndex++; } oNode = oNode.length == undefined ? [oNode] : oNode; oNode = oNode[0] ? oNode : null; while (oNode && oNode.length > 0 && aParts[iIndex]) { sPart = aParts[iIndex]; if (sPart.indexOf("@") == 0) { oNode = this._getAttribute(oNode[0], sPart.substr(1)); } else if (sPart == "text()") { oNode = oNode[0] ? oNode[0].textContent : ""; } else if (isNaN(sPart)) { oNode = this._getChildElementsByTagName(oNode[0], sPart); } else { oNode = [ oNode[sPart] ]; } iIndex++; } return oNode; }; /** * @param {string} sPath * @param {object} oContext * @returns {any} */ XMLModel.prototype._getAttribute = function (oNode, sName) { if (!this.oNameSpaces || sName.indexOf(":") == -1) { return oNode.getAttribute(sName); } var sNameSpace = this._getNameSpace(sName), sLocalName = this._getLocalName(sName); if (oNode.getAttributeNS) { return oNode.getAttributeNS(sNameSpace, sLocalName); } else { // IE8 if (!this.oDocNSPrefixes) { this.oDocNSPrefixes = this._getDocNSPrefixes(); } var sPrefix = this.oDocNSPrefixes[sNameSpace]; sName = (sPrefix ? sPrefix + ":" : "") + sLocalName; return oNode.getAttribute(sName); } }; /** * @param {object} oNode * @param {string} sName * @returns {any} */ XMLModel.prototype._getChildElementsByTagName = function (oNode, sName) { var aChildNodes = oNode.childNodes, aResult = []; if (this.oNameSpaces) { var sNameSpace = this._getNameSpace(sName), sLocalName = this._getLocalName(sName), sChildLocalName; each(aChildNodes, function(i, oChild){ sChildLocalName = oChild.localName || oChild.baseName; if (oChild.nodeType == 1 && sChildLocalName == sLocalName && oChild.namespaceURI == sNameSpace) { aResult.push(oChild); } }); } else { each(aChildNodes, function(i, oChild){ if (oChild.nodeType == 1 && oChild.nodeName == sName) { aResult.push(oChild); } }); } return aResult; }; /** * @param {string} sName * @returns {string} */ XMLModel.prototype._getNameSpace = function (sName) { var iColonPos = sName.indexOf(":"), sPrefix = ""; if (iColonPos > 0) { sPrefix = sName.substr(0, iColonPos); } return this.oNameSpaces[sPrefix]; }; /** * @param {string} sName * @returns {string} */ XMLModel.prototype._getLocalName = function (sName) { var iColonPos = sName.indexOf(":"), sLocalName = sName; if (iColonPos > 0) { sLocalName = sName.substr(iColonPos + 1); } return sLocalName; }; /** * @returns {object} */ XMLModel.prototype._getDocNSPrefixes = function () { var oPrefixes = {}, oDocumentElement = this.oData && this.oData.documentElement; if (!oDocumentElement) { return oPrefixes; } var aAttributes = oDocumentElement.attributes; each(aAttributes, function(i, oAttribute) { var name = oAttribute.name, value = oAttribute.value; if (name == "xmlns") { oPrefixes[value] = ""; } else if (name.indexOf("xmlns") == 0) { oPrefixes[value] = name.substr(6); } }); return oPrefixes; }; /** * Resolve the path relative to the given context */ XMLModel.prototype._resolve = function(sPath, oContext) { var bIsRelative = !sPath.startsWith("/"), sResolvedPath = sPath; if (bIsRelative) { if (oContext) { sResolvedPath = oContext.getPath() + "/" + sPath; } else { sResolvedPath = this.isLegacySyntax() ? "/" + sPath : undefined; } } return sResolvedPath; }; XMLModel.prototype.isList = function(sPath, oContext) { return false; }; /** * Sets the meta model associated with this model * * @private * @param {sap.ui.model.MetaModel} oMetaModel the meta model associated with this model */ XMLModel.prototype._setMetaModel = function(oMetaModel) { this._oMetaModel = oMetaModel; }; XMLModel.prototype.getMetaModel = function() { return this._oMetaModel; }; return XMLModel; });