UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

856 lines (784 loc) 34.7 kB
/*! * OpenUI5 * (c) Copyright 2026 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ /*eslint-disable max-len */ // Provides class sap.ui.model.odata.v2.ODataAnnotations sap.ui.define([ "sap/base/assert", "sap/base/i18n/Localization", "sap/base/util/extend", "sap/ui/base/EventProvider", "sap/ui/core/cache/CacheManager", "sap/ui/model/odata/AnnotationParser", "sap/ui/thirdparty/jquery" ], function(assert, Localization, extend, EventProvider, CacheManager, AnnotationParser, jQuery) { "use strict"; ///////////////////////////////////////////////// Class Definition ///////////////////////////////////////////////// /** * Creates a new instance of the ODataAnnotations annotation loader. * * @param {sap.ui.model.odata.ODataMetadata} oMetadata * Metadata object with the metadata information needed to parse the annotations * @param {object} mOptions Obligatory options * @param {string|map|string[]|map[]} mOptions.source * One or several annotation sources; see {@link #addSource} for more details * @param {Object<string,string>} mOptions.headers A map of headers to be sent with every request; * see {@link #setHeaders} for more details * @param {boolean} mOptions.skipMetadata Whether the metadata document will not be parsed for * annotations * @param {string} [mOptions.cacheKey] A valid cache key * @param {boolean} [mOptions.withCredentials=false] If set to <code>true</code>, the user credentials are included * in a cross-origin request * @public * * @class Annotation loader for OData V2 services * * @author SAP SE * @version 1.147.1 * * @public * @since 1.37.0 * @alias sap.ui.model.odata.v2.ODataAnnotations * @extends sap.ui.base.EventProvider */ var ODataAnnotations = EventProvider.extend("sap.ui.model.odata.v2.ODataAnnotations", /** @lends sap.ui.model.odata.v2.ODataAnnotations.prototype */ { constructor : function(oMetadata, mOptions) { var that = this; // Allow event subscription in constructor options EventProvider.apply(this, [ mOptions ]); this.bWithCredentials = mOptions?.withCredentials === true; this._oMetadata = oMetadata; // The promise to have (loaded,) parsed and merged the previously added source. // This promise should never reject; to assign another promise "pPromise" use // alwaysResolve(pPromise) this._pLoaded = oMetadata.loaded(); this._mCustomHeaders = {}; this._mAnnotations = {}; this._hasErrors = false; function writeCache(aResults) { // write annotations to cache if no errors occurred if (!that._hasErrors) { CacheManager.set(that.sCacheKey, JSON.stringify(aResults)); } } if (!mOptions || !mOptions.skipMetadata) { if (!mOptions) { mOptions = {}; } if (!mOptions.source) { mOptions.source = []; } else if (Array.isArray(mOptions.source)) { mOptions.source = mOptions.source.slice(0); } else { mOptions.source = [ mOptions.source ]; } mOptions.source.unshift({ type: "xml", data: oMetadata.loaded().then(function(mParams) { return { xml: mParams["metadataString"], lastModified: mParams["lastModified"], eTag: mParams["eTag"] }; }) }); } if (mOptions) { this.sCacheKey = mOptions.cacheKey; this.setHeaders(mOptions.headers); if (this.sCacheKey) { //check cache this._pLoaded = CacheManager.get(that.sCacheKey) .then(function(sAnnotations){ var aResults; if (sAnnotations) { aResults = JSON.parse(sAnnotations); } //old cache entries are an object; invalidate the cache in this case if (Array.isArray(aResults)) { // restore return array structure aResults.annotations = {}; aResults.forEach(function(oAnnotation) { AnnotationParser.restoreAnnotationsAtArrays(oAnnotation.annotations); AnnotationParser.merge(aResults.annotations, oAnnotation.annotations); }); that._mAnnotations = aResults.annotations; // only valid loading was cached - fire loaded event in this case that._fireSomeLoaded(aResults); that._fireLoaded(aResults); return aResults; } else { return that.addSource(mOptions.source) .then(function(aResults) { writeCache(aResults); return aResults; }); } }); } else { this._pLoaded = this.addSource(mOptions.source); } } }, metadata : { publicMethods : [ "getData", "addSource", "getHeaders", "setHeaders", "attachSuccess", "detachSuccess", "attachError", "detachError", "attachLoaded", "detachLoaded", "attachFailed", "detachFailed" ] } }); ////////////////////////////////////////////////// Public Methods ////////////////////////////////////////////////// /** * Returns the parsed and merged annotation data object. * * @public * @returns {object} The annotation data */ ODataAnnotations.prototype.getData = function() { return this._mAnnotations; }; /** * Returns the parsed and merged annotation data object. * * @public * @returns {object} The annotation data * @deprecated As of version 1.37.0, only kept for compatibility with V1 API, use {@link #getData} instead. */ ODataAnnotations.prototype.getAnnotationsData = ODataAnnotations.prototype.getData; /** * Returns a map of custom headers that are sent with every request to an annotation URL. * @public * @returns {Object<string,string>} A map of all custom headers. */ ODataAnnotations.prototype.getHeaders = function() { return extend({}, this._mCustomHeaders); }; /** * Set custom headers which are provided in a key/value map. These headers are used for all requests. * The "Accept-Language" header cannot be modified and is set using the core's language setting. * * To remove these headers, simply set the <code>mHeaders</code> parameter to <code>{}</code>. Note that when calling this method * again, all previous custom headers are removed, unless they are specified again in the <code>mCustomHeaders</code> parameter. * * @param {Object<string,string>} mHeaders the header name/value map. * @public */ ODataAnnotations.prototype.setHeaders = function(mHeaders) { // Copy headers (don't use reference to mHeaders map) this._mCustomHeaders = extend({}, mHeaders); }; /** * Returns a promise that resolves when the added annotation sources were successfully * processed. * * @returns {Promise<void>} A promise that resolves after the last added sources have been processed * @public */ ODataAnnotations.prototype.loaded = function() { return this._pLoaded; }; /** * An annotation source, containing either a URL to be loaded or an XML string to be parsed. * * @typedef {object} sap.ui.model.odata.v2.ODataAnnotations.Source * @property {string} type The source type. Either "url" or "xml". * @property {string|Promise} data The data or a Promise that resolves with the data. * In case the type is set to "url" the data must be a URL, in case it is set to "xml" the data must be * an XML string. * @property {string} [xml] (Set internally, available in event-callback) The XML string of the annotation source * @property {Document} [document] (Set internally, available in event-callback) The parsed XML document of the annotation source * @property {Object} [annotations] (Set internally, available in event-callback) The parsed Annotations object of the annotation source * @public */ /** * Adds one or several sources to the annotation loader. Sources will be loaded instantly but merged only after * the previously added source has either been successfully merged or failed. * * @param {string|string[]|sap.ui.model.odata.v2.ODataAnnotations.Source|sap.ui.model.odata.v2.ODataAnnotations.Source[]} vSource One or several * Annotation source or array of annotation sources; an annotation source is either a string * containing a URL or an object of type * {@link sap.ui.model.odata.v2.ODataAnnotations.Source}. * @returns {Promise<Array<{source: sap.ui.model.odata.v2.ODataAnnotations.Source, data: any}|Error>>} * The promise to (load,) parse and merge the given source(s). The Promise * resolves with an array of maps containing the properties <code>source</code> and * <code>data</code>; see the parameters of the <code>success</code> event for more * details. In case at least one source could not be (loaded,) parsed or merged, the promise * fails with an array of objects containing Errors and/or Success objects. * @public */ ODataAnnotations.prototype.addSource = function(vSource) { if (!vSource || Array.isArray(vSource) && vSource.length === 0) { //For compatibility return this._oMetadata.loaded(); } if (!Array.isArray(vSource)) { vSource = [ vSource ]; } var that = this; // Make sure aSources contains source objects not simple URL strings and create merge Promise array var aMergePromises = vSource.map(function(vAnnotationSource) { vAnnotationSource = (typeof vAnnotationSource === "string") ? { type: "url", data: vAnnotationSource } : vAnnotationSource; return that._loadSource(vAnnotationSource) .then(that._parseSourceXML) .then(that._parseSource.bind(that)) .catch(function(oError) { return oError; }); }); //merge parsed annotations in correct order return Promise.all(aMergePromises) .then(function(aAnnotations) { return aAnnotations.map(function(oAnnotation) { try { oAnnotation = that._mergeSource(oAnnotation); that._fireSuccess(oAnnotation); } catch (oError) { that._fireError(oAnnotation); } return oAnnotation; }); }) .then(function(aResults) { // Add for Promise compatibility with v1 version: aResults.annotations = that.getData(); var aErrors = aResults.filter(function(oResult) { return oResult instanceof Error; }); if (aErrors.length > 0) { that._hasErrors = true; if (aErrors.length !== aResults.length) { that._fireSomeLoaded(aResults); that._fireFailed(aResults); } else { that._fireFailed(aResults); that._fireAllFailed(aResults); return Promise.reject(aResults); } } else { that._fireSomeLoaded(aResults); that._fireLoaded(aResults); } return aResults; }); }; /////////////////////////////////////////////////// Event Methods ////////////////////////////////////////////////// /** * Parameters of the <code>success</code> event * * @typedef {object} sap.ui.model.odata.v2.ODataAnnotations.successParameters * @property {sap.ui.model.odata.v2.ODataAnnotations.Source} result The source type. Either "url" or "xml". * @public */ /** * The <code>success</code> event is fired, whenever a source has been successfully (loaded,) parsed and merged into the * annotation data. * * @name sap.ui.model.odata.v2.ODataAnnotations#success * @event * @param {sap.ui.base.Event} oControlEvent * @param {sap.ui.base.EventProvider} oControlEvent.getSource * @param {sap.ui.model.odata.v2.ODataAnnotations.successParameters} oControlEvent.getParameters * @public */ /** * Attaches the given callback to the {@link #event:success success} event, which is fired whenever a source has been successfully * (loaded,) parsed and merged into the annotation data. * * The following parameters are set on the event object that is given to the callback function: * <code>source</code> - A map containing the properties <code>type</code> - containing either "url" or "xml" - and <code>data</code> containing * the data given as source, either a URL or an XML string depending on how the source was added. * * When called, the context of the event handler (its <code>this</code>) will be bound to <code>oListener</code> * if specified, otherwise it will be bound to this <code>sap.ui.model.odata.v2.ODataAnnotations</code> itself. * * @param {object} * [oData] An application-specific payload object that will be passed to the event handler * along with the event object when firing the event * @param {function} * fnFunction The function to be called, when the event occurs * @param {object} * [oListener=this] Context object to call the event handler with, defaults to this * <code>ODataAnnotations</code> itself * @returns {this} Reference to <code>this</code> to allow method chaining * @public */ ODataAnnotations.prototype.attachSuccess = function(oData, fnFunction, oListener) { return this.attachEvent("success", oData, fnFunction, oListener); }; /** * Detaches the given callback from the <code>success</code> event. * * The passed function and listener object must match the ones used for event registration. * * @param {function} fnFunction The function to be called, when the event occurs * @param {object} [oListener] Context object on which the given function had to be called * @returns {this} Reference to <code>this</code> to allow method chaining * @public */ ODataAnnotations.prototype.detachSuccess = function(fnFunction, oListener) { return this.detachEvent("success", fnFunction, oListener); }; /** * Parameters of the <code>error</code> event. * * @typedef {object} sap.ui.model.odata.v2.ODataAnnotations.errorParameters * @property {Error} result The error that occurred. Also contains the properties from {@link sap.ui.model.odata.v2.ODataAnnotations.Source} * that could be filled up to that point * @public */ /** * The <code>error</code> event is fired, whenever a source cannot be loaded, parsed or merged into the annotation data. * * @name sap.ui.model.odata.v2.ODataAnnotations#error * @event * @param {sap.ui.base.Event} oEvent * @param {sap.ui.base.EventProvider} oEvent.getSource * @param {sap.ui.model.odata.v2.ODataAnnotations.errorParameters} oEvent.getParameters * @public */ /** * Attaches the given callback to the {@link #event:error error} event, which is fired whenever a source cannot be loaded, parsed or * merged into the annotation data. * * The following parameters will be set on the event object that is given to the callback function: * <code>source</code> - A map containing the properties <code>type</code> - containing either "url" or "xml" - and <code>data</code> containing * the data given as source, either a URL or an XML string depending on how the source was added. * <code>error</code> - An Error object describing the problem that occurred * * When called, the context of the event handler (its <code>this</code>) will be bound to <code>oListener</code> * if specified, otherwise it will be bound to this <code>sap.ui.model.odata.v2.ODataAnnotations</code> itself. * * @param {object} * [oData] An application-specific payload object that will be passed to the event handler * along with the event object when firing the event * @param {function} * fnFunction The function to be called, when the event occurs * @param {object} * [oListener=this] Context object to call the event handler with, defaults to this * <code>ODataAnnotations</code> itself * @returns {this} Reference to <code>this</code> to allow method chaining * @public */ ODataAnnotations.prototype.attachError = function(oData, fnFunction, oListener) { return this.attachEvent("error", oData, fnFunction, oListener); }; /** * Detaches the given callback from the <code>error</code> event. * * The passed function and listener object must match the ones used for event registration. * * @param {function} fnFunction The function to be called, when the event occurs * @param {object} [oListener] Context object on which the given function had to be called * @returns {this} Reference to <code>this</code> to allow method chaining * @public */ ODataAnnotations.prototype.detachError = function(fnFunction, oListener) { return this.detachEvent("error", fnFunction, oListener); }; /** * Parameters of the <code>loaded</code> event. * * @typedef {object} sap.ui.model.odata.v2.ODataAnnotations.loadedParameters * @property {sap.ui.model.odata.v2.ODataAnnotations.Source[]|Error[]|any} result * An array of results and Errors (see {@link sap.ui.model.odata.v2.ODataAnnotations#success} * and {@link sap.ui.model.odata.v2.ODataAnnotations#error}) that occurred while loading * a group of annotations * @public */ /** * The <code>loaded</code> event is fired, when all annotations from a group of sources have been * (loaded,) parsed and merged successfully. * * @name sap.ui.model.odata.v2.ODataAnnotations#loaded * @event * @param {sap.ui.base.Event} oEvent * @param {sap.ui.base.EventProvider} oEvent.getSource * @param {sap.ui.model.odata.v2.ODataAnnotations.loadedParameters} oEvent.getParameters * @public */ /** * Attaches the given callback to the {@link #event:loaded loaded} event. * * This event is fired when all annotations from a group of sources was successfully (loaded,) parsed and merged. * The parameter <code>result</code> will be set on the event argument and contains an array of all loaded sources as well * as Errors in the order in which they had been added. * * When called, the context of the event handler (its <code>this</code>) will be bound to <code>oListener</code> * if specified, otherwise it will be bound to this <code>sap.ui.model.odata.v2.ODataAnnotations</code> itself. * * @param {object} * [oData] An application-specific payload object that will be passed to the event handler * along with the event object when firing the event * @param {function} * fnFunction The function to be called, when the event occurs * @param {object} * [oListener=this] Context object to call the event handler with, defaults to this * <code>ODataAnnotations</code> itself * @returns {this} Reference to <code>this</code> to allow method chaining * @public */ ODataAnnotations.prototype.attachLoaded = function(oData, fnFunction, oListener) { return this.attachEvent("loaded", oData, fnFunction, oListener); }; /** * Detaches the given callback from the <code>loaded</code> event. * * The passed function and listener object must match the ones used for event registration. * * @param {function} fnFunction The function to be called, when the event occurs * @param {object} [oListener] Context object on which the given function had to be called * @returns {this} Reference to <code>this</code> to allow method chaining * @public */ ODataAnnotations.prototype.detachLoaded = function(fnFunction, oListener) { return this.detachEvent("loaded", fnFunction, oListener); }; /** * Parameters of the <code>failed</code> event. * * @typedef {object} sap.ui.model.odata.v2.ODataAnnotations.failedParameters * @property {Error[]} result An array of Errors, see {@link sap.ui.model.odata.v2.ODataAnnotations#error} that occurred while * loading a group of annotations * @public */ /** * The <code>failed</code> event is fired when at least one annotation from a group of sources was not * successfully (loaded,) parsed or merged. * * @name sap.ui.model.odata.v2.ODataAnnotations#failed * @event * @param {sap.ui.base.Event} oEvent * @param {sap.ui.base.EventProvider} oEvent.getSource * @param {sap.ui.model.odata.v2.ODataAnnotations.failedParameters} oEvent.getParameters * @public */ /** * Attaches the given callback to the {@link #event:failed failed} event. * * This event is fired when at least one annotation from a group of sources was not successfully (loaded,) parsed or merged. * The parameter <code>result</code> will be set on the event argument and contains an array of Errors in the order in which * the sources had been added. * * When called, the context of the event handler (its <code>this</code>) will be bound to <code>oListener</code> * if specified, otherwise it will be bound to this <code>sap.ui.model.odata.v2.ODataAnnotations</code> itself. * * @param {object} * [oData] An application-specific payload object that will be passed to the event handler * along with the event object when firing the event * @param {function} * fnFunction The function to be called, when the event occurs * @param {object} * [oListener=this] Context object to call the event handler with, defaults to this * <code>ODataAnnotations</code> itself * @returns {this} Reference to <code>this</code> to allow method chaining * @public */ ODataAnnotations.prototype.attachFailed = function(oData, fnFunction, oListener) { return this.attachEvent("failed", oData, fnFunction, oListener); }; /** * Detaches the given callback from the <code>failed</code> event. * * The passed function and listener object must match the ones used for event registration. * * @param {function} fnFunction The function to be called, when the event occurs * @param {object} [oListener] Context object on which the given function had to be called * @returns {this} Reference to <code>this</code> to allow method chaining * @public */ ODataAnnotations.prototype.detachFailed = function(fnFunction, oListener) { return this.detachEvent("failed", fnFunction, oListener); }; /** * Attaches the given callback to the <code>someLoaded</code> event. * * This event exists for compatibility with the old annotation loader. It is fired when at least one annotation * from a group of sources was successfully (loaded,) parsed and merged. * The parameter <code>result</code> will be set on the event argument and contains an array of all loaded * sources as well as Errors in the order in which they had been added. * * When called, the context of the event handler (its <code>this</code>) will be bound to <code>oListener</code> * if specified, otherwise it will be bound to this <code>sap.ui.model.odata.v2.ODataAnnotations</code> itself. * * @param {object} * [oData] An application-specific payload object that will be passed to the event handler * along with the event object when firing the event * @param {function} * fnFunction The function to be called, when the event occurs * @param {object} * [oListener=this] Context object to call the event handler with, defaults to this * <code>ODataAnnotations</code> itself * @returns {this} Reference to <code>this</code> to allow method chaining * @public */ ODataAnnotations.prototype.attachSomeLoaded = function(oData, fnFunction, oListener) { return this.attachEvent("someLoaded", oData, fnFunction, oListener); }; /** * Detaches the given callback from the <code>someLoaded</code> event. * * The passed function and listener object must match the ones used for event registration. * * @param {function} fnFunction The function to be called, when the event occurs * @param {object} [oListener] Context object on which the given function had to be called * @returns {this} Reference to <code>this</code> to allow method chaining * @public */ ODataAnnotations.prototype.detachSomeLoaded = function(fnFunction, oListener) { return this.detachEvent("someLoaded", fnFunction, oListener); }; /** * Attaches the given callback to the <code>allFailed</code> event. * * This event exists for compatibility with the old Annotation loader. It is fired when no annotation from a group * of sources was successfully (loaded,) parsed and merged. * The parameter <code>result</code> will be set on the event argument and contains an array of Errors in the order * in which the sources had been added. * * When called, the context of the event handler (its <code>this</code>) will be bound to <code>oListener</code> * if specified, otherwise it will be bound to this <code>sap.ui.model.odata.v2.ODataAnnotations</code> itself. * * @param {object} * [oData] An application-specific payload object that will be passed to the event handler * along with the event object when firing the event * @param {function} * fnFunction The function to be called, when the event occurs * @param {object} * [oListener=this] Context object to call the event handler with, defaults to this * <code>ODataAnnotations</code> itself * @returns {this} Reference to <code>this</code> to allow method chaining * @public */ ODataAnnotations.prototype.attachAllFailed = function(oData, fnFunction, oListener) { return this.attachEvent("allFailed", oData, fnFunction, oListener); }; /** * Detaches the given callback from the <code>allFailed</code> event. * * The passed function and listener object must match the ones used for event registration. * * @param {function} fnFunction The function to be called, when the event occurs * @param {object} [oListener] Context object on which the given function had to be called * @returns {this} Reference to <code>this</code> to allow method chaining * @public */ ODataAnnotations.prototype.detachAllFailed = function(fnFunction, oListener) { return this.detachEvent("allFailed", fnFunction, oListener); }; ////////////////////////////////////////////////// Private Methods ///////////////////////////////////////////////// /** * Fires the <code>success</code> event whenever a source has successfully been (loaded,) parsed and merged into the annotation * data. * * @param {Object} mResult The filled source-map of the successful loading and parsing * @returns {this} Reference to <code>this</code> to allow method chaining * @private */ ODataAnnotations.prototype._fireSuccess = function(mResult) { return this.fireEvent("success", { result: mResult }, false, false); }; /** * Fires the <code>error</code> event whenever a source could not be (loaded,) parsed or merged into the annotation * data. * * @param {Error} oError The error that occurred. * @return {this} Returns <code>this</code> to allow method chaining. * @private */ ODataAnnotations.prototype._fireError = function(oError) { return this.fireEvent("error", { result: oError }, false, false); }; /** * Fires the <code>loaded</code> event with an array of results in the result-parameter of the event. * * @param {sap.ui.model.odata.v2.ODataAnnotations.Source[]} aResults An array of results * @return {this} Returns <code>this</code> to allow method chaining. * @private */ ODataAnnotations.prototype._fireLoaded = function(aResults) { return this.fireEvent("loaded", { result: aResults }, false, false); }; /** * Fires the <code>failed</code> event with an array of results and errors in the result-parameter of the event. * * @param {Error[]} aErrors An array of Errors * @return {this} Returns <code>this</code> to allow method chaining. * @private */ ODataAnnotations.prototype._fireFailed = function(aErrors) { return this.fireEvent("failed", { result: aErrors }, false, false); }; /** * Fires the <code>someLoaded</code> event with an array of results and errors in the result-parameter of the event. * * @param {sap.ui.model.odata.v2.ODataAnnotations.Source[]|Error[]|any} aResults An array of results and Errors * @return {this} Returns <code>this</code> to allow method chaining. * @private */ ODataAnnotations.prototype._fireSomeLoaded = function(aResults) { return this.fireEvent("someLoaded", { result: aResults }, false, false); }; /** * Fires the <code>failed</code> event with an array of errors in the result-parameter of the event. * * @param {Error[]} aErrors An array of Errors * @return {this} Returns <code>this</code> to allow method chaining. * @private */ ODataAnnotations.prototype._fireAllFailed = function(aErrors) { return this.fireEvent("allFailed", { result: aErrors }, false, false); }; /** * Loads a given source (sap.ui.model.odata.v2.ODataAnnotations.Source) if necessary and returns a promise that resolves * if the source could be loaded or no loading is necessary. In case the source type is neither "xml" nor "url" or * the loading of the source fails, the promise rejects. * * @param {sap.ui.model.odata.v2.ODataAnnotations.Source} mSource The source to be loaded * @returns {Promise} The Promise to load the source if necessary * @private */ ODataAnnotations.prototype._loadSource = function(mSource) { if (mSource.data instanceof Promise) { return mSource.data.then(function(oData) { delete mSource.data; mSource.type = "xml"; mSource.xml = oData.xml; mSource.lastModified = oData.lastModified; mSource.eTag = oData.eTag; return this._loadSource(mSource); }.bind(this)); } else if (mSource.type === "xml") { if (typeof mSource.data === "string") { mSource.xml = mSource.data; delete mSource.data; } return Promise.resolve(mSource); } else if (mSource.type === "url") { return this._loadUrl(mSource); } else { var oError = new Error("Unknown source type: \"" + mSource.type + "\""); oError.source = mSource; return Promise.reject(oError); } }; /** * Loads a source with the type "url" and returns a promise that resolves if the source was successfully loaded or * rejects in case of failure. * * @param {sap.ui.model.odata.v2.ODataAnnotations.Source} mSource The source of type "url" to be loaded * @returns {Promise} The Promise to load the source * @private */ ODataAnnotations.prototype._loadUrl = function(mSource) { assert(mSource.type === "url", "Source type must be \"url\" in order to be loaded"); return new Promise((fnResolve, fnReject) => { var mAjaxOptions = { url: mSource.data, async: true, headers: this._getHeaders(), ...(this.bWithCredentials === true ? {xhrFields: {withCredentials: true}} : {}), beforeSend: function(oXHR) { // Force text/plain so the XML parser does not run twice oXHR.overrideMimeType("text/plain"); } }; var fnSuccess = function(sData, sStatusText, oXHR) { mSource.xml = oXHR.responseText; if (oXHR.getResponseHeader("Last-Modified")) { // no need to use UI5Date.getInstance as only the UTC timestamp is relevant mSource.lastModified = new Date(oXHR.getResponseHeader("Last-Modified")); } if (oXHR.getResponseHeader("eTag")) { mSource.eTag = oXHR.getResponseHeader("eTag"); } fnResolve(mSource); }; var fnFail = function(oXHR, sStatusText) { var oError = new Error("Could not load annotation URL: \"" + mSource.data + "\""); oError.source = mSource; fnReject(oError); }; jQuery.ajax(mAjaxOptions).done(fnSuccess).fail(fnFail); }); }; /** * Parses a source as XML and returns a promise that resolves when the source's <code>xml</code> property string could be * successfully parsed as an XML document. * * @param {sap.ui.model.odata.v2.ODataAnnotations.Source} mSource The source that should be parsed with its <code>xml</code> property set to a string * @returns {Promise} The Promise to parse the source as XML * @private */ ODataAnnotations.prototype._parseSourceXML = function(mSource) { assert(typeof mSource.xml === "string", "Source must contain XML string in order to be parsed"); return new Promise(function(fnResolve, fnReject) { var oXMLDocument = new DOMParser().parseFromString(mSource.xml, 'application/xml'); // Check for errors if (oXMLDocument.getElementsByTagName("parsererror").length > 0) { var oError = new Error("There were errors parsing the XML."); oError.source = { type: mSource.type, data: mSource.data, xml: mSource.xml, document: oXMLDocument }; fnReject(oError); } else { mSource.document = oXMLDocument; fnResolve(mSource); } }); }; /** * Parses a source that has been parsed to an XML document as annotations and returns a promise that resolves when * the source's <code>document</code> property could be successfully parsed as an annotations object. * * @param {sap.ui.model.odata.v2.ODataAnnotations.Source} mSource The source that should be parsed with its <code>document</code> property set to an XML document * @returns {Promise} The Promise to parse the source as an annotations object * @private */ ODataAnnotations.prototype._parseSource = function(mSource) { return this._oMetadata.loaded() .then(function() { mSource.annotations = AnnotationParser.parse(this._oMetadata, mSource.document, mSource.data); //delete document as it is not needed anymore after parsing delete mSource.document; return mSource; }.bind(this)); }; /** * Merges the parsed annotation object of the given source into the internal annotations object. The source's * <code>annotations</code> property must contain an annotations object. * * @param {sap.ui.model.odata.v2.ODataAnnotations.Source} mSource The source that should be parsed with its <code>annotations</code> property set to an annotations object * @returns {Promise} The Promise to merge the source's annotations object into the internal annotations object * @private */ ODataAnnotations.prototype._mergeSource = function(mSource) { assert(typeof mSource.annotations === "object", "Source must contain an annotation object to be merged"); AnnotationParser.merge(this._mAnnotations, mSource.annotations); return mSource; }; /** * Returns a map of the public and private headers that are sent with every request to an annotation URL. * @private * @returns {Object<string,string>} A map of all public and private headers. */ ODataAnnotations.prototype._getHeaders = function() { // The 'sap-cancel-on-close' header marks the OData annotation request as cancelable. This helps to save // resources at the back-end. return extend({"sap-cancel-on-close": "true"}, this.getHeaders(), { "Accept-Language": Localization.getLanguageTag().toString() // Always overwrite }); }; ///////////////////////////////////////////////////// End Class //////////////////////////////////////////////////// return ODataAnnotations; });