@openui5/sap.ui.core
Version:
OpenUI5 Core Library sap.ui.core
1,482 lines (1,323 loc) • 191 kB
JavaScript
/*!
* OpenUI5
* (c) Copyright 2026 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Disable some ESLint rules. camelcase (some "_" in names to indicate indexed variables (like in
// math)), valid-jsdoc (not completed yet), no-warning-comments (some TODOs are left)
// All other warnings, errors should be resolved
/*eslint-disable camelcase, valid-jsdoc, no-warning-comments, max-len */
// Provides API for analytical extensions in OData service metadata
sap.ui.define([
"sap/base/security/encodeURL",
"sap/ui/model/Filter",
"sap/ui/model/FilterOperator",
"sap/ui/model/Sorter",
"sap/ui/model/odata/ODataUtils"
], function(encodeURL, Filter, FilterOperator, Sorter, ODataUtils) {
"use strict";
/**
* The OData4Analytics API is purely experimental, not yet functionally complete
* and not meant for productive usage. At present, its only purpose is to
* demonstrate how easy analytical extensions of OData4SAP can be consumed.
*
* <em>USE OBJECTS VIA METHODS ONLY - DO NOT ACCESS JAVASCRIPT OBJECT PROPERTIES DIRECTLY !</em>
*
* Lazy initialization of attributes will cause unexpected values when you
* access object attributes directly.
*
* @author SAP SE
* @deprecated As of version 1.138.0, will be replaced by OData V4 data aggregation, see
* {@link topic:7d914317c0b64c23824bf932cc8a4ae1 Extension for Data Aggregation}
* @namespace
* @alias sap.ui.model.analytics.odata4analytics
* @protected
*/
var odata4analytics = odata4analytics || {},
rOnlyDigits = /^\d+$/;
odata4analytics.constants = {};
odata4analytics.constants["SAP_NAMESPACE"] = "http://www.sap.com/Protocols/SAPData";
odata4analytics.constants["VERSION"] = "0.7";
odata4analytics.helper = {
/*
* @param {object[]} [aOldColumns]
* @param {object[]} aNewColumns
* @param {function} [fnFormatterChanged]
* called for each column where only a formatter has changed
* @returns {number} 0: same, 1: only formatters changed, 2: important changes
*/
deepEqual : function (aOldColumns, aNewColumns, fnFormatterChanged) {
var oNewColumn,
oOldColumn,
iResult = 0,
i,
n;
if (!aOldColumns || aOldColumns.length !== aNewColumns.length) {
return 2;
}
if (aOldColumns !== aNewColumns) {
for (i = 0, n = aOldColumns.length; i < n; i += 1) {
oOldColumn = aOldColumns[i];
oNewColumn = aNewColumns[i];
if (oOldColumn.grouped !== oNewColumn.grouped
|| oOldColumn.inResult !== oNewColumn.inResult
|| oOldColumn.level !== oNewColumn.level
|| oOldColumn.name !== oNewColumn.name
|| oOldColumn.total !== oNewColumn.total
|| oOldColumn.visible !== oNewColumn.visible) {
return 2;
}
if (oOldColumn.formatter !== oNewColumn.formatter) {
iResult = 1;
if (fnFormatterChanged) {
fnFormatterChanged(oNewColumn);
}
}
}
}
return iResult;
},
/*
* Old helpers that got replaced by robust functions provided by the UI5 ODataModel
*/
/*
renderPropertyKeyValue : function(sKeyValue, sPropertyEDMTypeName) {
if (typeof sKeyValue == "string" && sKeyValue.charAt(0) == "'")
throw "Illegal property value starting with a quote";
switch (sPropertyEDMTypeName) {
case 'Edm.String':
return "'" + sKeyValue + "'";
case 'Edm.DateTime':
return "datetime'" + sKeyValue + "'";
case 'Edm.Guid':
return "guid'" + sKeyValue + "'";
case 'Edm.Time':
return "time'" + sKeyValue + "'";
case 'Edm.DateTimeOffset':
return "datetimeoffset'" + sKeyValue + "'";
default:
return sKeyValue;
}
},
renderPropertyFilterValue : function(sFilterValue, sPropertyEDMTypeName) {
if (typeof sFilterValue == "string" && sFilterValue.charAt(0) == "'")
throw "Illegal property value starting with a quote";
switch (sPropertyEDMTypeName) {
case 'Edm.String':
return "'" + sFilterValue + "'";
case 'Edm.DateTime':
return "datetime'" + sFilterValue + "'";
case 'Edm.Guid':
return "guid'" + sFilterValue + "'";
case 'Edm.Time':
return "time'" + sFilterValue + "'";
case 'Edm.DateTimeOffset':
return "datetimeoffset'" + sFilterValue + "'";
default:
return sFilterValue;
}
},
*/
tokenizeNametoLabelText : function(sName) {
var sLabel = "";
// remove leading 'P_' often used for parameter properties on HANA
sLabel = sName.replace(/^P_(.*)/, "$1");
// split UpperCamelCase in words (treat numbers and _ as upper case)
sLabel = sLabel.replace(/([^A-Z0-9_]+)([A-Z0-9_])/g, "$1 $2");
// split acronyms in words
sLabel = sLabel.replace(/([A-Z0-9_]{2,})([A-Z0-9_])([^A-Z0-9_]+)/g, "$1 $2$3");
// remove trailing _E
sLabel = sLabel.replace(/(.*) _E$/, "$1");
// remove underscores that were identified as upper case
sLabel = sLabel.replace(/(.*) _(.*)/g, "$1 $2");
return sLabel;
}
};
/**
* Create a representation of the analytical semantics of OData service metadata
*
* @param {object} oModelReference
* An instance of {@link sap.ui.model.analytics.odata4analytics.Model.ReferenceByModel} or
* {@link sap.ui.model.analytics.odata4analytics.Model.ReferenceWithWorkaround} for locating
* the OData service. {@link sap.ui.model.analytics.odata4analytics.Model.ReferenceByURI} is
* deprecated.
* @param {object} [mParameter]
* Additional parameters for controlling the model construction. Currently supported are:
* <li> sAnnotationJSONDoc - A JSON document providing extra annotations to the elements of
* the structure of the given service
* </li>
* <li> modelVersion (deprecated) - Parameter to define which ODataModel version should be
* used if you use {@link sap.ui.model.analytics.odata4analytics.Model.ReferenceByURI};
* supported values are: 1 (default), 2
* </li>
*
* @class Representation of an OData model with analytical annotations defined
* by OData4SAP.
* @name sap.ui.model.analytics.odata4analytics.Model
* @public
*/
odata4analytics.Model = function(oModelReference, mParameter) {
this._init(oModelReference, mParameter);
};
/**
* Create a reference to an OData model by the URI of the related OData service.
*
* @param {string}
* sURI holding the URI.
*
* @class Handle to an OData model by the URI pointing to it.
* @name sap.ui.model.analytics.odata4analytics.Model.ReferenceByURI
* @deprecated Since 1.94 use
* {@link sap.ui.model.analytics.odata4analytics.Model.ReferenceByModel} instead
* @public
*/
odata4analytics.Model.ReferenceByURI = function(sURI) {
return {
sServiceURI : sURI
};
};
/**
* Create a reference to an OData model already loaded elsewhere with the help
* of SAPUI5.
*
* @param {object}
* oModel holding the OData model.
*
* @class Handle to an already instantiated SAPUI5 OData model.
* @name sap.ui.model.analytics.odata4analytics.Model.ReferenceByModel
* @public
*/
odata4analytics.Model.ReferenceByModel = function(oModel) {
return {
oModel : oModel
};
};
/**
* Create a reference to an OData model having certain workarounds activated. A
* workaround is an implementation that changes the standard behavior of the API
* to overcome some gap or restriction in the OData provider. The workaround
* implementation can be conditionally activated by passing the identifier in
* the constructor.
*
* Known workaround identifiers are:
*
* <li>"CreateLabelsFromTechnicalNames" - If a property has no label text, it
* gets generated from the property name.</li>
*
* <li>"IdentifyTextPropertiesByName" -If a dimension property has no text and
* another property with the same name and an appended "Name", "Text" etc.
* exists, they are linked via annotation.</li>
*
*
* @param {object} oModel
* Holds a reference to the OData model, obtained by
* {@link sap.ui.model.analytics.odata4analytics.Model.ReferenceByModel}, or by
* {@link sap.ui.model.analytics.odata4analytics.Model.ReferenceByURI} which is deprecated.
* @param {string[]} aWorkaroundID
* All workarounds to be applied.
*
* @class Handle to an already instantiated SAPUI5 OData model.
* @name sap.ui.model.analytics.odata4analytics.Model.ReferenceWithWorkaround
* @public
*/
odata4analytics.Model.ReferenceWithWorkaround = function(oModel, aWorkaroundID) {
return {
oModelReference : oModel,
aWorkaroundID : aWorkaroundID
};
};
odata4analytics.Model.prototype = {
/**
* initialize a new object
*
* @private
*/
_init : function(oModelReference, mParameter) {
var ODataModelClass,
that = this;
if (typeof mParameter == "string") {
throw "Deprecated second argument: Adjust your invocation by passing an object with a property sAnnotationJSONDoc as a second argument instead";
}
this._mParameter = mParameter;
/*
* get access to OData model
*/
this._oActivatedWorkarounds = {};
if (oModelReference && oModelReference.aWorkaroundID) {
for (var i = -1, sID; (sID = oModelReference.aWorkaroundID[++i]) !== undefined;) {
this._oActivatedWorkarounds[sID] = true;
}
oModelReference = oModelReference.oModelReference;
}
// check proper usage
if (!oModelReference || (!oModelReference.sServiceURI && !oModelReference.oModel)) {
throw "Usage with oModelReference being an instance of Model.ReferenceByURI or Model.ReferenceByModel";
}
//check if a model is given, or we need to create one from the service URI
if (oModelReference.oModel) {
this._oModel = oModelReference.oModel;
checkForMetadata();
}
/** @deprecated As of version 1.94.0 */
if (oModelReference.sServiceURI) {
if (mParameter && mParameter.modelVersion === 2) {
// Check if the user wants a V2 model
ODataModelClass = sap.ui.require("sap/ui/model/odata/v2/ODataModel") ||
sap.ui.requireSync("sap/ui/model/odata/v2/ODataModel"); // legacy-relevant: fallback for missing dependency
this._oModel = new ODataModelClass(oModelReference.sServiceURI);
checkForMetadata();
} else {
//default is V1 Model
ODataModelClass = sap.ui.require("sap/ui/model/odata/ODataModel") ||
sap.ui.requireSync("sap/ui/model/odata/ODataModel"); // legacy-relevant: fallback for missing dependency
this._oModel = new ODataModelClass(oModelReference.sServiceURI);
checkForMetadata();
}
}
if (this._oModel.getServiceMetadata()
&& this._oModel.getServiceMetadata().dataServices == undefined) {
throw "Model could not be loaded";
}
/**
* Check if the metadata is already available, if not defere the interpretation of the Metadata
*/
function checkForMetadata() {
// V2 supports asynchronous loading of metadata
// we have to register for the MetadataLoaded Event in case, the data is not loaded already
if (!that._oModel.getServiceMetadata()) {
that._oModel.attachMetadataLoaded(processMetadata);
} else {
// metadata already loaded
processMetadata();
}
}
/**
* Kickstart the interpretation of the metadata,
* either called directly if metadata is available, or deferred and then
* executed via callback by the model during the metadata loaded event.
*/
function processMetadata () {
//only interprete the metadata if the analytics model was not initialised yet
if (that.bIsInitialized) {
return;
}
//mark analytics model as initialized
that.bIsInitialized = true;
/*
* add extra annotations if provided
*/
if (mParameter && mParameter.sAnnotationJSONDoc) {
that.mergeV2Annotations(mParameter.sAnnotationJSONDoc);
}
that._interpreteMetadata(that._oModel.getServiceMetadata().dataServices);
}
},
/**
* @private
*/
_interpreteMetadata: function (oMetadata) {
/*
* parse OData model for analytic queries
*/
this._oQueryResultSet = {};
this._oParameterizationSet = {};
this._oEntityTypeSet = {};
this._oEntitySetSet = {};
this._oEntityTypeNameToEntitySetMap = {};
// loop over all schemas and entity containers
// TODO: extend this implementation to support many schemas
var oSchema = this._oModel.getServiceMetadata().dataServices.schema[0];
// remember default container
for (var j = -1, oContainer; (oContainer = oSchema.entityContainer[++j]) !== undefined;) {
if (oContainer.isDefaultEntityContainer == "true") {
this._oDefaultEntityContainer = oContainer;
break;
}
}
var aEntityType = oSchema.entityType;
// A. preparation
// A.1 collect all relevant OData entity types representing query
// results, parameters
var aQueryResultEntityTypes = [], aParameterEntityTypes = [], aUnsortedEntityTypes = [];
for (var k = -1, oType; (oType = aEntityType[++k]) !== undefined;) {
var bProcessed = false;
if (oType.extensions != undefined) {
for (var l = -1, oExtension; (oExtension = oType.extensions[++l]) !== undefined;) {
if (oExtension.namespace == odata4analytics.constants.SAP_NAMESPACE
&& oExtension.name == "semantics") {
bProcessed = true;
switch (oExtension.value) {
case "aggregate":
aQueryResultEntityTypes.push(oType);
break;
case "parameters":
aParameterEntityTypes.push(oType);
break;
default:
aUnsortedEntityTypes.push(oType);
}
}
if (bProcessed) {
continue;
}
}
if (!bProcessed) {
aUnsortedEntityTypes.push(oType);
}
} else {
aUnsortedEntityTypes.push(oType);
}
}
// A.2 create entity type representations for the unsorted types
for (var m = -1, oType2; (oType2 = aUnsortedEntityTypes[++m]) !== undefined;) {
var oEntityType = new odata4analytics.EntityType(this._oModel.getServiceMetadata(), oSchema, oType2);
this._oEntityTypeSet[oEntityType.getQName()] = oEntityType;
var aEntitySet = this._getEntitySetsOfType(oSchema, oEntityType.getQName());
if (aEntitySet.length == 0) {
throw "Invalid consumption model: No entity set for entity type " + oEntityType.getQName() + " found";
}
if (aEntitySet.length > 1) {
throw "Unsupported consumption model: More than one entity set for entity type " + oEntityType.getQName() + " found";
}
var oEntitySet = new odata4analytics.EntitySet(this._oModel.getServiceMetadata(), oSchema,
aEntitySet[0][0], aEntitySet[0][1], oEntityType);
this._oEntitySetSet[oEntitySet.getQName()] = oEntitySet;
this._oEntityTypeNameToEntitySetMap[oEntityType.getQName()] = oEntitySet;
}
// B. create objects for the analytical extensions of these entity types
// B.1 create parameters
// temporary storage for lookup of entity *types* annotated with
// parameters semantics
var oParameterizationEntityTypeSet = {};
for (var n = -1, oType3; (oType3 = aParameterEntityTypes[++n]) !== undefined;) {
// B.1.1 create object for OData entity type
var oEntityType2 = new odata4analytics.EntityType(this._oModel.getServiceMetadata(), oSchema, oType3);
this._oEntityTypeSet[oEntityType2.getQName()] = oEntityType2;
// B.1.2 get sets with this type
var aEntitySet2 = this._getEntitySetsOfType(oSchema, oEntityType2.getQName());
if (aEntitySet2.length == 0) {
throw "Invalid consumption model: No entity set for parameter entity type " + oEntityType2.getQName() + " found";
}
if (aEntitySet2.length > 1) {
throw "Unsupported consumption model: More than one entity set for parameter entity type " + oEntityType2.getQName() + " found";
}
// B.1.3 create object for OData entity set
var oEntitySet2 = new odata4analytics.EntitySet(this._oModel.getServiceMetadata(), oSchema,
aEntitySet2[0][0], aEntitySet2[0][1], oEntityType2);
this._oEntitySetSet[oEntitySet2.getQName()] = oEntitySet2;
this._oEntityTypeNameToEntitySetMap[oEntityType2.getQName()] = oEntitySet2;
// B.1.4 create object for parameters and related OData entity
var oParameterization = new odata4analytics.Parameterization(oEntityType2, oEntitySet2);
this._oParameterizationSet[oParameterization.getName()] = oParameterization;
oParameterizationEntityTypeSet[oEntityType2.getQName()] = oParameterization;
// B.1.5 recognize all available parameter value helps
var sParameterizationEntityTypeQTypeName = oEntityType2.getQName();
if (oSchema.association != undefined) {
for (var p = -1, oAssoc; (oAssoc = oSchema.association[++p]) !== undefined;) {
// value help always established by a referential constraint
// on an association
if (oAssoc.referentialConstraint == undefined) {
continue;
}
var sParameterValueHelpEntityTypeQTypeName = null;
// B.1.5.1 relevant only if one end has same type as the
// given parameterization entity type
if (oAssoc.end[0].type == sParameterizationEntityTypeQTypeName && oAssoc.end[0].multiplicity == "*"
&& oAssoc.end[1].multiplicity == "1") {
sParameterValueHelpEntityTypeQTypeName = oAssoc.end[1].type;
} else if (oAssoc.end[1].type == sParameterizationEntityTypeQTypeName && oAssoc.end[1].multiplicity == "*"
&& oAssoc.end[0].multiplicity == "1") {
sParameterValueHelpEntityTypeQTypeName = oAssoc.end[0].type;
}
if (!sParameterValueHelpEntityTypeQTypeName) {
continue;
}
// B.1.5.2 check if the referential constraint declares a
// parameter property as dependent
if (oAssoc.referentialConstraint.dependent.propertyRef.length != 1) {
continue;
}
var oParameter = oParameterization.findParameterByName(oAssoc.referentialConstraint.dependent.propertyRef[0].name);
if (oParameter == null) {
continue;
}
// B.1.5.3 Register the recognized parameter value help
// entity type and set and link them to the parameter
var oValueListEntityType = this._oEntityTypeSet[sParameterValueHelpEntityTypeQTypeName];
var oValueListEntitySet = this._oEntityTypeNameToEntitySetMap[sParameterValueHelpEntityTypeQTypeName];
oParameter.setValueSetEntity(oValueListEntityType, oValueListEntitySet);
}
}
}
// B.2
// B.2 create analytic queries
for (var r = -1, oType4; (oType4 = aQueryResultEntityTypes[++r]) !== undefined;) {
// B.2.1 create object for OData entity
var oEntityType3 = new odata4analytics.EntityType(this._oModel.getServiceMetadata(), oSchema, oType4);
this._oEntityTypeSet[oEntityType3.getQName()] = oEntityType3;
var sQueryResultEntityTypeQTypeName = oEntityType3.getQName();
// B.2.2 find assocs to parameter entity types
var oParameterization3 = null;
var oAssocFromParamsToResult = null;
if (oSchema.association != undefined) {
for (var s = -1, oAssoc2; (oAssoc2 = oSchema.association[++s]) !== undefined;) {
var sParameterEntityTypeQTypeName = null;
if (oAssoc2.end[0].type == sQueryResultEntityTypeQTypeName) {
sParameterEntityTypeQTypeName = oAssoc2.end[1].type;
} else if (oAssoc2.end[1].type == sQueryResultEntityTypeQTypeName) {
sParameterEntityTypeQTypeName = oAssoc2.end[0].type;
} else {
continue;
}
// B.2.2.2 fetch Parameterization object if any
var oMatchingParameterization = null;
oMatchingParameterization = oParameterizationEntityTypeSet[sParameterEntityTypeQTypeName];
if (oMatchingParameterization != null) {
if (oParameterization3 != null) {
// TODO: extend this implementation to support more
// than one related parameter entity type
throw "Unable to handle multiple parameter entity types of query entity "
+ oEntityType3.name;
} else {
oParameterization3 = oMatchingParameterization;
oAssocFromParamsToResult = oAssoc2;
}
}
}
}
// B.2.3 get sets with this type
var aEntitySet3 = this._getEntitySetsOfType(oSchema, oEntityType3.getQName());
if (aEntitySet3.length != 1) {
throw "Invalid consumption model: There must be exactly one entity set for an entity type annotated with aggregating semantics";
}
// B.2.4 create object for OData entity set of analytic query result
var oEntitySet3 = new odata4analytics.EntitySet(this._oModel.getServiceMetadata(), oSchema,
aEntitySet3[0][0], aEntitySet3[0][1], oEntityType3);
this._oEntitySetSet[oEntitySet3.getQName()] = oEntitySet3;
this._oEntityTypeNameToEntitySetMap[oEntityType3.getQName()] = oEntitySet3;
// B.2.5 create object for analytic query result, related OData
// entity type and set and (if any) related parameters
// object
var oQueryResult = new odata4analytics.QueryResult(this, oEntityType3, oEntitySet3, oParameterization3);
this._oQueryResultSet[oQueryResult.getName()] = oQueryResult;
// B.2.6 set target result for found parameterization
if (oParameterization3) {
oParameterization3.setTargetQueryResult(oQueryResult, oAssocFromParamsToResult);
}
// B.2.7 recognize all available dimension value helps
if (oSchema.association != undefined) {
for (var t = -1, oAssoc3; (oAssoc3 = oSchema.association[++t]) !== undefined;) {
// value help always established by a referential constraint
// on an association
if (oAssoc3.referentialConstraint == undefined) {
continue;
}
var sDimensionValueHelpEntityTypeQTypeName = null;
// B.2.7.1 relevant only if one end has same type as the
// given query result entity type
if (oAssoc3.end[0].type == sQueryResultEntityTypeQTypeName && oAssoc3.end[0].multiplicity == "*"
&& oAssoc3.end[1].multiplicity == "1") {
sDimensionValueHelpEntityTypeQTypeName = oAssoc3.end[1].type;
} else if (oAssoc3.end[1].type == sQueryResultEntityTypeQTypeName && oAssoc3.end[1].multiplicity == "*"
&& oAssoc3.end[0].multiplicity == "1") {
sDimensionValueHelpEntityTypeQTypeName = oAssoc3.end[0].type;
}
if (!sDimensionValueHelpEntityTypeQTypeName) {
continue;
}
// B.2.7.2 check if the referential constraint declares a
// dimension property as dependent
if (oAssoc3.referentialConstraint.dependent.propertyRef.length != 1) {
continue;
}
var oDimension = oQueryResult.findDimensionByName(oAssoc3.referentialConstraint.dependent.propertyRef[0].name);
if (oDimension == null) {
continue;
}
// B.2.7.3 Register the recognized dimension value help
// entity set and link it to the dimension
var oDimensionMembersEntitySet = this._oEntityTypeNameToEntitySetMap[sDimensionValueHelpEntityTypeQTypeName];
oDimension.setMembersEntitySet(oDimensionMembersEntitySet);
}
}
}
},
/*
* Control data for adding extra annotations to service metadata
*
* @private
*/
oUI5ODataModelAnnotatableObject : {
objectName : "schema",
keyPropName : "namespace",
extensions : true,
aSubObject : [ {
objectName : "entityType",
keyPropName : "name",
extensions : true,
aSubObject : [ {
objectName : "property",
keyPropName : "name",
aSubObject : [],
extensions : true
} ]
}, {
objectName : "entityContainer",
keyPropName : "name",
extensions : false,
aSubObject : [ {
objectName : "entitySet",
keyPropName : "name",
extensions : true,
aSubObject : []
} ]
} ]
},
/*
* merging extra annotations with provided service metadata
*
* @private
*/
mergeV2Annotations : function(sAnnotationJSONDoc) {
var oAnnotation = null;
try {
oAnnotation = JSON.parse(sAnnotationJSONDoc);
} catch (exception) {
return;
}
var oMetadata;
try {
oMetadata = this._oModel.getServiceMetadata().dataServices;
} catch (exception) {
return;
}
// find "schema" entry in annotation document
for ( var propName in oAnnotation) {
if (!(this.oUI5ODataModelAnnotatableObject.objectName == propName)) {
continue;
}
if (!(oAnnotation[propName] instanceof Array)) {
continue;
}
this.mergeV2AnnotationLevel(oMetadata[this.oUI5ODataModelAnnotatableObject.objectName],
oAnnotation[this.oUI5ODataModelAnnotatableObject.objectName], this.oUI5ODataModelAnnotatableObject);
break;
}
return;
},
/*
* merging extra annotations with a given service metadata object
*
* @private
*/
mergeV2AnnotationLevel : function(aMetadata, aAnnotation, oUI5ODataModelAnnotatableObject) {
for (var i = -1, oAnnotation; (oAnnotation = aAnnotation[++i]) !== undefined;) {
for (var j = -1, oMetadata; (oMetadata = aMetadata[++j]) !== undefined;) {
if (!(oAnnotation[oUI5ODataModelAnnotatableObject.keyPropName] == oMetadata[oUI5ODataModelAnnotatableObject.keyPropName])) {
continue;
}
// found match: apply extensions from oAnnotation object to
// oMetadata object
if (oAnnotation["extensions"] != undefined) {
if (oMetadata["extensions"] == undefined) {
oMetadata["extensions"] = [];
}
for (var l = -1, oAnnotationExtension; (oAnnotationExtension = oAnnotation["extensions"][++l]) !== undefined;) {
var bFound = false;
for (var m = -1, oMetadataExtension; (oMetadataExtension = oMetadata["extensions"][++m]) !== undefined;) {
if (oAnnotationExtension.name == oMetadataExtension.name
&& oAnnotationExtension.namespace == oMetadataExtension.namespace) {
oMetadataExtension.value = oAnnotationExtension.value;
bFound = true;
break;
}
}
if (!bFound) {
oMetadata["extensions"].push(oAnnotationExtension);
}
}
}
// walk down to sub objects
for (var k = -1, oUI5ODataModelAnnotatableSubObject; (oUI5ODataModelAnnotatableSubObject = oUI5ODataModelAnnotatableObject.aSubObject[++k]) !== undefined;) {
for ( var propName in oAnnotation) {
if (!(oUI5ODataModelAnnotatableSubObject.objectName == propName)) {
continue;
}
if (!(oAnnotation[oUI5ODataModelAnnotatableSubObject.objectName] instanceof Array)) {
continue;
}
if ((oMetadata[oUI5ODataModelAnnotatableSubObject.objectName] == undefined)
|| (!(oMetadata[oUI5ODataModelAnnotatableSubObject.objectName] instanceof Array))) {
continue;
}
this.mergeV2AnnotationLevel(oMetadata[oUI5ODataModelAnnotatableSubObject.objectName],
oAnnotation[oUI5ODataModelAnnotatableSubObject.objectName], oUI5ODataModelAnnotatableSubObject);
break;
}
}
}
}
return;
},
/**
* Find analytic query result by name
*
* @param {string}
* sName Fully qualified name of query result entity set
* @returns {sap.ui.model.analytics.odata4analytics.QueryResult} The query result object
* with this name or null if it does not exist
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.Model#findQueryResultByName
*/
findQueryResultByName : function(sName) {
var oQueryResult = this._oQueryResultSet[sName];
// Everybody should have a second chance:
// If the name was not fully qualified, check if it is in the default
// container
if (!oQueryResult && this._oDefaultEntityContainer) {
var sQName = this._oDefaultEntityContainer.name + "." + sName;
oQueryResult = this._oQueryResultSet[sQName];
}
return oQueryResult;
},
/**
* Get the names of all query results (entity sets) offered by the model
*
* @returns {string[]} List of all query result names
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.Model#getAllQueryResultNames
*/
getAllQueryResultNames : function() {
if (this._aQueryResultNames) {
return this._aQueryResultNames;
}
this._aQueryResultNames = new Array(0);
for ( var sName in this._oQueryResultSet) {
this._aQueryResultNames.push(this._oQueryResultSet[sName].getName());
}
return this._aQueryResultNames;
},
/**
* Get all query results offered by the model
*
* @returns {object} An object with individual JS properties for each query
* result included in the model. The JS object properties all are
* objects of type odata4analytics.QueryResult. The names
* of the JS object properties are given by the entity set names
* representing the query results.
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.Model#getAllQueryResults
*/
getAllQueryResults : function() {
return this._oQueryResultSet;
},
/**
* Get underlying OData model provided by SAPUI5
*
* @returns {object} The SAPUI5 representation of the model.
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.Model#getODataModel
*/
getODataModel : function() {
return this._oModel;
},
/**
* Private methods
*/
/**
* Find entity sets of a given type
*
* @private
*/
_getEntitySetsOfType : function(oSchema, sQTypeName) {
var aEntitySet = [];
for (var i = -1, oEntityContainer; (oEntityContainer = oSchema.entityContainer[++i]) !== undefined;) {
for (var j = -1, oEntitySet; (oEntitySet = oEntityContainer.entitySet[++j]) !== undefined;) {
if (oEntitySet.entityType == sQTypeName) {
aEntitySet.push([ oEntityContainer, oEntitySet ]);
}
}
}
return aEntitySet;
},
/**
* Private member attributes
*/
_mParameter : null,
_oModel : null,
_oDefaultEntityContainer : null,
_aQueryResultNames : null,
_oQueryResultSet : null,
_oParameterizationSet : null,
_oEntityTypeSet : null,
_oEntitySetSet : null,
_oEntityTypeNameToEntitySetMap : null,
_oActivatedWorkarounds : null
};
/** ******************************************************************** */
/**
* Create a representation of an analytic query. Do not create your own instances.
*
* @param {sap.ui.model.analytics.odata4analytics.Model}
* oModel The analytical model containing this query result entity
* set
* @param {sap.ui.model.analytics.odata4analytics.EntityType}
* oEntityType The OData entity type for this query
* @param {sap.ui.model.analytics.odata4analytics.EntitySet}
* oEntitySet The OData entity set for this query offered by the
* OData service
* @param {sap.ui.model.analytics.odata4analytics.Parameterization}
* oParameterization The parameterization of this query, if any
*
* @this (QueryResult)
*
* @class Representation of an entity type annotated with
* sap:semantics="aggregate".
* @name sap.ui.model.analytics.odata4analytics.QueryResult
* @public
*/
odata4analytics.QueryResult = function(oModel, oEntityType, oEntitySet, oParameterization) {
this._init(oModel, oEntityType, oEntitySet, oParameterization);
};
odata4analytics.QueryResult.prototype = {
/**
* initialize new object
*
* @private
*/
_init : function(oModel, oEntityType, oEntitySet, oParameterization, oAssocFromParamsToResult) {
this._oModel = oModel;
this._oEntityType = oEntityType;
this._oEntitySet = oEntitySet;
this._oParameterization = oParameterization;
this._oDimensionSet = {};
this._oMeasureSet = {};
// parse entity type for analytic semantics described by annotations
var aProperty = oEntityType.getTypeDescription().property;
var oAttributeForPropertySet = {};
for (var i = -1, oProperty; (oProperty = aProperty[++i]) !== undefined;) {
if (oProperty.extensions == undefined) {
continue;
}
for (var j = -1, oExtension; (oExtension = oProperty.extensions[++j]) !== undefined;) {
if (!oExtension.namespace == odata4analytics.constants.SAP_NAMESPACE) {
continue;
}
switch (oExtension.name) {
case "aggregation-role":
switch (oExtension.value) {
case "dimension": {
var oDimension = new odata4analytics.Dimension(this, oProperty);
this._oDimensionSet[oDimension.getName()] = oDimension;
break;
}
case "measure": {
var oMeasure = new odata4analytics.Measure(this, oProperty);
this._oMeasureSet[oMeasure.getName()] = oMeasure;
break;
}
case "totaled-properties-list":
this._oTotaledPropertyListProperty = oProperty;
break;
default:
}
break;
case "attribute-for": {
var oDimensionAttribute = new odata4analytics.DimensionAttribute(this, oProperty);
var oKeyProperty = oDimensionAttribute.getKeyProperty();
oAttributeForPropertySet[oKeyProperty.name] = oDimensionAttribute;
break;
}
default:
}
}
}
// assign dimension attributes to the respective dimension objects
for ( var sDimensionAttributeName in oAttributeForPropertySet) {
var oDimensionAttribute2 = oAttributeForPropertySet[sDimensionAttributeName];
oDimensionAttribute2.getDimension().addAttribute(oDimensionAttribute2);
}
// apply workaround for missing text properties if requested
if (oModel._oActivatedWorkarounds.IdentifyTextPropertiesByName) {
var aMatchedTextPropertyName = [];
for ( var oDimName in this._oDimensionSet) {
var oDimension2 = this._oDimensionSet[oDimName];
if (!oDimension2.getTextProperty()) {
var oTextProperty = null; // order of matching is
// significant!
oTextProperty = oEntityType.findPropertyByName(oDimName + "Name");
if (!oTextProperty) {
oTextProperty = oEntityType.findPropertyByName(oDimName + "Text");
}
if (!oTextProperty) {
oTextProperty = oEntityType.findPropertyByName(oDimName + "Desc");
}
if (!oTextProperty) {
oTextProperty = oEntityType.findPropertyByName(oDimName + "Description");
}
if (oTextProperty) { // any match?
oDimension2.setTextProperty(oTextProperty); // link
// dimension
// with text
// property
aMatchedTextPropertyName.push(oTextProperty.name);
}
}
}
// make sure that any matched text property is not exposed as
// dimension (according to spec)
for (var t = -1, sPropertyName; (sPropertyName = aMatchedTextPropertyName[++t]) !== undefined;) {
delete this._oDimensionSet[sPropertyName];
}
}
},
/**
* Get the name of the query result
*
* @returns {string} The fully qualified name of the query result, which is
* identical with the name of the entity set representing the query
* result in the OData service
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.QueryResult#getName
*/
getName : function() {
return this.getEntitySet().getQName();
},
/**
* Get the parameterization of this query result
*
* @returns {sap.ui.model.analytics.odata4analytics.Parameterization} The object for the
* parameterization or null if the query result is not
* parameterized
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.QueryResult#getParameterization
*/
getParameterization : function() {
return this._oParameterization;
},
/**
* Get the names of all dimensions included in the query result
*
* @returns {string[]} List of all dimension names
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.QueryResult#getAllDimensionNames
*/
getAllDimensionNames : function() {
if (this._aDimensionNames) {
return this._aDimensionNames;
}
this._aDimensionNames = [];
for ( var sName in this._oDimensionSet) {
this._aDimensionNames.push(this._oDimensionSet[sName].getName());
}
return this._aDimensionNames;
},
/**
* Get all dimensions included in this query result
*
* @returns {object} An object with individual JS properties for each
* dimension included in the query result. The JS object properties
* all are objects of type odata4analytics.Dimension. The
* names of the JS object properties are given by the OData entity
* type property names representing the dimension keys.
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.QueryResult#getAllDimensions
*/
getAllDimensions : function() {
return this._oDimensionSet;
},
/**
* Get the names of all measures included in the query result
*
* @returns {string[]} List of all measure names
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.QueryResult#getAllMeasureNames
*/
getAllMeasureNames : function() {
if (this._aMeasureNames) {
return this._aMeasureNames;
}
this._aMeasureNames = [];
for ( var sName in this._oMeasureSet) {
this._aMeasureNames.push(this._oMeasureSet[sName].getName());
}
return this._aMeasureNames;
},
/**
* Get all measures included in this query result
*
* @returns {object} An object with individual JS properties for each
* measure included in the query result. The JS object properties
* all are objects of type odata4analytics.Measure. The
* names of the JS object properties are given by the OData entity
* type property names representing the measure raw values.
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.QueryResult#getAllMeasures
*/
getAllMeasures : function() {
return this._oMeasureSet;
},
/**
* Find dimension by name
*
* @param {string}
* sName Dimension name
* @returns {sap.ui.model.analytics.odata4analytics.Dimension} The dimension object with
* this name or null if it does not exist
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.QueryResult#findDimensionByName
*/
findDimensionByName : function(sName) {
return this._oDimensionSet[sName];
},
/**
* Find dimension by property name
*
* @param {string}
* sName Property name
* @returns {sap.ui.model.analytics.odata4analytics.Dimension} The dimension object to
* which the given property name is related, because the property
* holds the dimension key, its text, or is an attribute of this
* dimension. If no such dimension exists, null is returned.
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.QueryResult#findDimensionByPropertyName
*/
findDimensionByPropertyName : function(sName) {
if (this._oDimensionSet[sName]) { // the easy case
return this._oDimensionSet[sName];
}
for ( var sDimensionName in this._oDimensionSet) {
var oDimension = this._oDimensionSet[sDimensionName];
var oTextProperty = oDimension.getTextProperty();
if (oTextProperty && oTextProperty.name == sName) {
return oDimension;
}
if (oDimension.findAttributeByName(sName)) {
return oDimension;
}
}
return null;
},
/**
* Get property holding the totaled property list
*
* @returns {object} The datajs object representing this property
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.QueryResult#getTotaledPropertiesListProperty
*/
getTotaledPropertiesListProperty : function() {
return this._oTotaledPropertyListProperty;
},
/**
* Find measure by name
*
* @param {string}
* sName Measure name
* @returns {sap.ui.model.analytics.odata4analytics.Measure} The measure object with this
* name or null if it does not exist
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.QueryResult#findMeasureByName
*/
findMeasureByName : function(sName) {
return this._oMeasureSet[sName];
},
/**
* Find measure by property name
*
* @param {string}
* sName Property name
* @returns {sap.ui.model.analytics.odata4analytics.Measure} The measure object to which
* the given property name is related, because the property holds
* the raw measure value or its formatted value. If no such measure
* exists, null is returned.
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.QueryResult#findMeasureByPropertyName
*/
findMeasureByPropertyName : function(sName) {
if (this._oMeasureSet[sName]) { // the easy case
return this._oMeasureSet[sName];
}
for ( var sMeasureName in this._oMeasureSet) {
var oMeasure = this._oMeasureSet[sMeasureName];
var oFormattedValueProperty = oMeasure.getFormattedValueProperty();
if (oFormattedValueProperty && oFormattedValueProperty.name == sName) {
return oMeasure;
}
}
return null;
},
/**
* Get the analytical model containing the entity set for this query result
*
* @returns {object} The analytical representation of the OData model
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.QueryResult#getModel
*/
getModel : function() {
return this._oModel;
},
/**
* Get the entity type defining the type of this query result in the OData
* model
*
* @returns {sap.ui.model.analytics.odata4analytics.EntityType} The OData entity type for
* this query result
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.QueryResult#getEntityType
*/
getEntityType : function() {
return this._oEntityType;
},
/**
* Get the entity set representing this query result in the OData model
*
* @returns {sap.ui.model.analytics.odata4analytics.EntitySet} The OData entity set
* representing this query result
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.QueryResult#getEntitySet
*/
getEntitySet : function() {
return this._oEntitySet;
},
/**
* Private member attributes
*/
_oModel : null,
_oEntityType : null,
_oEntitySet : null,
_oParameterization : null,
_aDimensionNames : null,
_oDimensionSet : null,
_aMeasureNames : null,
_oMeasureSet : null,
_oTotaledPropertyListProperty : null
};
/** ******************************************************************** */
/**
* Create a representation of a parameterization for an analytic query. Do not create your own instances.
*
* @param {sap.ui.model.analytics.odata4analytics.EntityType}
* oEntityType The OData entity type for this parameterization
* @param {sap.ui.model.analytics.odata4analytics.EntitySet}
* oEntitySet The OData entity set for this parameterization offered
* by the OData service
*
* @class Representation of an entity type annotated with
* sap:semantics="parameters".
* @name sap.ui.model.analytics.odata4analytics.Parameterization
* @public
*/
odata4analytics.Parameterization = function(oEntityType, oEntitySet) {
this._init(oEntityType, oEntitySet);
};
odata4analytics.Parameterization.prototype = {
/**
* @private
*/
_init : function(oEntityType, oEntitySet) {
this._oEntityType = oEntityType;
this._oEntitySet = oEntitySet;
this._oParameterSet = {};
// parse entity type for analytic semantics described by annotations
var aProperty = oEntityType.getTypeDescription().property;
for (var i = -1, oProperty; (oProperty = aProperty[++i]) !== undefined;) {
if (oProperty.extensions == undefined) {
continue;
}
for (var j = -1, oExtension; (oExtension = oProperty.extensions[++j]) !== undefined;) {
if (!oExtension.namespace == odata4analytics.constants.SAP_NAMESPACE) {
continue;
}
switch (oExtension.name) {
// process parameter semantics
case "parameter": {
var oParameter = new odata4analytics.Parameter(this, oProperty);
this._oParameterSet[oParameter.getName()] = oParameter;
break;
}
default:
}
}
}
},
// to be called only by Model objects
setTargetQueryResult : function(oQueryResult, oAssociation) {
this._oQueryResult = oQueryResult;
var sQAssocName = this._oEntityType.getSchema().namespace + "." + oAssociation.name;
var aNavProp = this._oEntityType.getTypeDescription().navigationProperty;
if (!aNavProp) {
throw "Invalid consumption model: Parameters entity type lacks navigation property for association to query result entity type";
}
for (var i = -1, oNavProp; (oNavProp = aNavProp[++i]) !== undefined;) {
if (oNavProp.relationship == sQAssocName) {
this._oNavPropToQueryResult = oNavProp.name;
}
}
if (!this._oNavPropToQueryResult) {
throw "Invalid consumption model: Parameters entity type lacks navigation property for association to query result entity type";
}
},
getTargetQueryResult : function() {
if (!this._oQueryResult) {
throw "No target query result set";
}
return this._oQueryResult;
},
/**
* Get the name of the parameter
*
* @returns {string} The name of the parameterization, which is identical
* with the name of the entity set representing the
* parameterization in the OData service
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.Parameterization#getName
*/
getName : function() {
return this.getEntitySet().getQName();
},
/**
* Get the names of all parameters part of the parameterization
*
* @returns {string[]} List of all parameter names
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.Parameterization#getAllParameterNames
*/
getAllParameterNames : function() {
if (this._aParameterNames) {
return this._aParameterNames;
}
this._aParameterNames = [];
for ( var sName in this._oParameterSet) {
this._aParameterNames.push(this._oParameterSet[sName].getName());
}
return this._aParameterNames;
},
/**
* Get all parameters included in this parameterization
*
* @returns {object} An object with individual JS properties for each
* parameter included in the query result. The JS object properties
* all are objects of type odata4analytics.Parameter. The
* names of the JS object properties are given by the OData entity
* type property names representing the parameter keys.
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.Parameterization#getAllParameters
*/
getAllParameters : function() {
return this._oParameterSet;
},
/**
* Find parameter by name
*
* @param {string}
* sName Parameter name
* @returns {sap.ui.model.analytics.odata4analytics.Parameter} The parameter object with
* this name or null if it does not exist
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.Parameterization#findParameterByName
*/
findParameterByName : function(sName) {
return this._oParameterSet[sName];
},
/**
* Get navigation property to query result
*
* @returns {sap.ui.model.analytics.odata4analytics.QueryResult} The parameter object with
* this name or null if it does not exist
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.Parameterization#getNavigationPropertyToQueryResult
*/
getNavigationPropertyToQueryResult : function() {
return this._oNavPropToQueryResult;
},
/**
* Get the entity type defining the type of this query result in the OData
* model
*
* @returns {sap.ui.model.analytics.odata4analytics.EntityType} The OData entity type for
* this query result
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.Parameterization#getEntityType
*/
getEntityType : function() {
return this._oEntityType;
},
/**
* Get the entity set representing this query result in the OData model
*
* @returns {sap.ui.model.analytics.odata4analytics.EntitySet} The OData entity set
* representing this query result
* @public
* @function
* @name sap.ui.model.analytics.odata4analytics.Parameterization#getEntitySet
*/
getEntitySet : function() {
return this._oEntitySet;
},
/**
* Private member attributes
*/
_oEntityType : null,
_oEntitySet : null,
_oQueryResult : null,
_oNavPropToQueryResult : null,
_aParameterNames : null,
_oParameterSet : null
};
/** ******************************************************************** */
/**
* Create a representation of a single parameter contained in a parameterization. Do not create your own instances.
*
* @param {sap.ui.model.analytics.odata4analytics.Parameterization}
* oParameterization The parameterization containing this parameter
* @param {object}
* oProperty The datajs object object representing the text property
*
* @class Representation of a property annotated with sap:parameter.
* @name sap.ui.model.analytics.odata4analytics.Parameter
* @public
*/
odata4analytics.Parameter = function(oParameterization, oProperty) {
this._init(oParameterization, oProperty);
};
odata4analytics.Parameter.prototype = {
/**
* @private
*/
_init : function(oParameterization, oProperty) {
this._oParameterization = oParameterization;
this._oProperty = oProperty;
var oEntityType = oParameterization.getEntityType();
if (oProperty.extensions != undefined) {
for (var i = -1, oExtension; (oExtension = oProperty.extensions[++i]) !== undefined;) {
if (!oExtension.namespace == odata4analytics.constants.SAP_NAMESPACE) {
continue;
}
switch (oExtension.name) {
case "parameter":
switch (oExtension.value) {
case "mandatory