Source: impl/inav2/sina_impl.js

/*
 * @file Simple info access (SINA) API: Implementation of the SINA interface
 * for the INA V2 service.
 * @namespace sap.bc.ina.api.sina.impl.inav2.sina_impl
 * @requires sap.bc.ina.api.sina
 * @requires sap.bc.ina.api.sina.impl.inav2.filter
 * @requires sap.bc.ina.api.sina.impl.inav2.system
 * @requires sap.bc.ina.api.sina.impl.inav2.proxy
 * @requires jQuery
 * @copyright Copyright (c) 2013 SAP AG. All rights reserved.
 */

(function(global, isXS) {

  "use strict";

  var executeSinaImpl = function($) {
    // =========================================================================
    // create packages
    // =========================================================================
    if (!$) {
      $ = global.$;
    }

    if (!isXS) {
      global.sap.bc.ina.api.sina.expandPackage(global, 'sap.bc.ina.api.sina.impl.inav2.sina_impl');
      global.sap.bc.ina.api.sina.expandPackage(global, 'sap.bc.ina.api.sina.impl.inav2.proxy');
    }

    var module = global.sap.bc.ina.api.sina.impl.inav2.sina_impl;
    module.Sina = function(){this.init.apply(this,arguments);};
    module.Facet = function(){this.init.apply(this,arguments);};
    module.Perspective = function(){this.init.apply(this,arguments);};
    module.Perspective2 = function(){this.init.apply(this,arguments);};
    module.Query = function(){this.init.apply(this,arguments);};
    module.CatalogQuery = function(){this.init.apply(this,arguments);};
    module.CatalogResultSet = function(){this.init.apply(this,arguments);};
    module.PerspectiveQuery = function(){this.init.apply(this,arguments);};
    module.PerspectiveGetQuery = function(){this.init.apply(this,arguments);};
    module.PerspectiveSearchQuery = function(){this.init.apply(this,arguments);};
    module.PerspectiveSearchResultSet = function(){this.init.apply(this,arguments);};
    module.Suggestion = function(){this.init.apply(this,arguments);};
    module.SuggestionQuery = function(){this.init.apply(this,arguments);};
    module.SuggestionResultSet = function(){this.init.apply(this,arguments);};
    module.Suggestion2Query = function(){this.init.apply(this,arguments);};
    module.Suggestion2ResultSet = function(){this.init.apply(this,arguments);};
    module.ChartQuery = function(){this.init.apply(this,arguments);};
    module.ChartResultSet = function(){this.init.apply(this,arguments);};
    module.GroupBarChartQuery = function(){this.init.apply(this,arguments);};
    module.GroupBarChartResultSet = function(){this.init.apply(this,arguments);};
    module.LineChartQuery = function(){this.init.apply(this,arguments);};
    module.LineChartResultSet = function(){this.init.apply(this,arguments);};
    module.SearchQuery = function(){this.init.apply(this,arguments);};
    module.SearchResultSet = function() { this.init.apply(this, arguments); };

    module.SuggestionAutoQuery = function(properties) {
      if (properties.system instanceof global.sap.bc.ina.api.sina.impl.inav2.system.ABAPSystem) {
        $.extend(this, module.Suggestion2Query.prototype);
      }
      if (properties.system instanceof global.sap.bc.ina.api.sina.impl.inav2.system.HANASystem) {
        $.extend(this, module.SuggestionQuery.prototype);
      }
      this.init.apply(this,arguments);
    };

    // =========================================================================
    // import packages
    // =========================================================================
    var jsontemplates = global.sap.bc.ina.api.sina.impl.inav2.jsontemplates;
    var filter = global.sap.bc.ina.api.sina.impl.inav2.filter;
    // var system = global.sap.bc.ina.api.sina.impl.inav2.system;
    var config = global.sap.bc.ina.api.sina.impl.inav2.proxy.config;
    var sinabase = global.sap.bc.ina.api.sina.sinabase;
    module.Condition = filter.Condition;

    // =========================================================================
    // register provider
    // =========================================================================
    module.IMPL_TYPE = "inav2";
    sinabase.registerProvider({
      impl_type: module.IMPL_TYPE,
      sina: module.Sina,
      chartQuery: module.ChartQuery,
      searchQuery: module.SearchQuery,
      groupBarChartQuery: module.GroupBarChartQuery,
      lineChartQuery: module.LineChartQuery,
      suggestionQuery: module.SuggestionAutoQuery,
      perspectiveQuery: module.PerspectiveQuery,
      perspectiveSearchQuery: module.PerspectiveSearchQuery,
      perspectiveGetQuery: module.PerspectiveGetQuery,
      Facet: module.Facet,
      DataSource: filter.DataSource,
      Filter: filter.Filter,
      FilterCondition: filter.Condition,
      FilterConditionGroup: filter.ConditionGroup
    });

    // =========================================================================
    // sina
    // =========================================================================
    module.Sina.prototype = $.extend({}, sinabase.Sina.prototype, {

      /**
       * Creates a new instance of SINA that uses the INA V2 service. Use
       * the SINA factory {@link sap.bc.ina.api.sina.getSina} instead of this
       * private constructor.
       * @private
       * @augments {sap.bc.ina.api.sina.sinabase.Sina}
       * @this {sap.bc.ina.api.sina.impl.inav2.sina_impl.Sina}
       * @constructs sap.bc.ina.api.sina.impl.inav2.sina_impl.Sina
       * @param  {Object} properties Configuration properties for the instance.
       * @since SAP HANA SPS 06
       * @ignore
       */
      init: function(properties) {
        properties = properties || {};
        this.sinaSystem(properties.system || new global.sap.bc.ina.api.sina.impl.inav2.system.HANASystem());
        sinabase.Sina.prototype.init.apply(this, arguments);
      },

      /**
       * Gets or sets an SAP client. Only used with system type ABAP.
       * @ignore
       * @since SAP HANA SPS 06
       * @memberOf sap.bc.ina.api.sina.impl.inav2.sina_impl.Sina
       * @instance
       * @param  {Integer} sapclient Number of the SAP client to be used with the service.
       * @return {Integer} The SAP client number that is currently set, but only if called
       * without a parameter.
       */
      sapclient: function(sapclient) {
        var sys;
        if (sapclient) {
          if (sapclient < 0) {
            sys = new global.sap.bc.ina.api.sina.impl.inav2.system.HANASystem();
          } else {
            sys = new global.sap.bc.ina.api.sina.impl.inav2.system.ABAPSystem({
              "sapclient": sapclient
            });
          }
          this.sinaSystem(sys);
        } else {
          return this.sinaSystem().properties.sapclient();
        }
        return {};
      },

      _registerPostProcessor: function(postProcessor) {
        if (!this.postProcessor) {
          this.postProcessor = [];
        }
        this.postProcessor.push(postProcessor);
      },

      _postprocess: function(sinAction, sourceTitle) {
        if (this.postProcessor) {
          for (var i = 0; i < this.postProcessor.length; i++) {
            this.postProcessor[i](sinAction, sourceTitle);
          }
        }
      },

    });

    // =========================================================================
    // class query. Base for chart, search and perspective queries
    // =========================================================================
    module.Query.prototype = $.extend({}, {

      /**
       *  The base query for chart, search, and suggestions queries.
       *  Use the sap.bc.ina.api.sina.create*Query factory methods instead of this class.
       *  @constructs sap.bc.ina.api.sina.impl.inav2.sina_impl.Query
       *  @private
       *  @ignore
       */
      init: function(properties) {
        this.filter = properties.filter || new filter.Filter({
          dataSource: properties.dataSource
        });
      },

      _resetResultSet: function() {
        this.deferredResultSet = null;
        this.resultSet = null;
        this.jqXHR = null;
      },

      _getSinaError: function(data) {

        var error = {};
        if (data.error) {

          var e = JSON.parse(error.responseText);
          // error.message = e.error.message;
          // error.code = e.error.code;
          // return error;

          return new sinabase.SinaError({
            message: e.error.message,
            errorCode: e.error.code
          });

        }

        // if (data.Messages) {
        //   for (var i = 0; i < data.Messages.length; i++) {

        //     if (data.Messages[i].Type >= 2) {
        //       return new sinabase.SinaError({
        //         message: data.Messages[i].Text,
        //         errorCode: 0
        //       });

        //     }
        //   }
        // }

        return null;

      },

      setDataSource: function(dataSource) {
          if (dataSource === undefined) {
              return this;
          }
          dataSource = new filter.DataSource(dataSource);
          if (this.filter.getDataSource() === undefined) {
              this.filter.setDataSource(dataSource);
              this._resetResultSet();
              return this;
          }
          if (!this.filter.getDataSource().equals(dataSource)) {
              this.filter.setDataSource(dataSource);
              this._resetResultSet();
          }
          return this;
      },
      
      execute: function() {
        var self = this;

        var jsonRequest = self.createJsonRequest();

        self.system.getServerInfo();

        var jqXHR = self._fireRequest(jsonRequest, true);

        var deferredResultSet = $.Deferred();

        jqXHR.done(function(data) {
          var error = self._getSinaError(data);
          if (data && error) {
            deferredResultSet.reject(error);
            return;
          }
          var resultSet = new self.resultSetClass(self.resultSetProperties);
          resultSet.setJsonData(data);
          deferredResultSet.resolve(resultSet);
        })
          .fail(function(error) {
            deferredResultSet.reject(error);
          })
          .always(function(data) {
            self._responseJson = data;
          });
        return deferredResultSet.promise();
      },

      executeSync: function() {
        var self = this;

        if (!isXS && self.resultSet !== null) {
          return self.resultSet;
        }
        var jsonRequest = self.createJsonRequest();
        self.system.getServerInfo();
        var data = self._fireRequest(jsonRequest, false);
        self._responseJson = data;
        self.resultSet = new self.resultSetClass(self.resultSetProperties);

        if (!isXS && data && data.responseText) {
          var response = JSON.parse(data.responseText);
          self.resultSet.setJsonData(response);
        } else if (data && data.arguments) {
          self.resultSet.setJsonData(data.arguments[0]);
        }

        return self.resultSet;
      },

      /**
       * Calls the server for the query instances.
       * @private
       * @ignore
       * @memberOf sap.bc.ina.api.sina.impl.inav2.Query
       * @instance
       * @param  {Object} jsonRequest   Request data that is sent to the server.
       * @param  {boolean} async        Should the request be asynchronous?
       */
      _fireRequest: function(jsonRequest, async) {

        var request = {
          async: async,
          url: this.system.inaUrl,
          contentType: 'application/json',
          dataType: 'json',
          data: jsonRequest
        };

        var jqXHR = this.system.proxy.ajax(request);
        return jqXHR;

      },

      /**
       * Assembles the order-by expression needed for calling ina.v2 service.
       * @private
       * @ignore
       * @memberOf sap.bc.ina.api.sina.impl.inav2.Query
       * @instance
       * @return {Object} Order-by expression.
       */
      _assembleOrderBy: function() {

        var orderByList = [];

        function orderByToInaSyntax(orderBy, sortOrder) {
          var inaOrderObj = {};
          if (orderBy.toLowerCase() === '$$score$$') {
            inaOrderObj.Function = 'Score';
          } else {
            inaOrderObj.AttributeName = orderBy;
          }
          inaOrderObj.SortOrder = sortOrder.toUpperCase();
          return inaOrderObj;
        }

        var orderObj;
        if ($.type(this.orderBy) === 'array') {
          for (var j = 0; j < this.orderBy.length; j++) {
            orderObj = orderByToInaSyntax(this.orderBy[j].orderBy, this.orderBy[j].sortOrder);
            orderByList.push(orderObj);
          }
        } else if ($.type(this.orderBy) === 'object' && this.orderBy.orderBy) {
          orderObj = orderByToInaSyntax(this.orderBy.orderBy, this.orderBy.sortOrder);
          orderByList.push(orderObj);
        }

        return orderByList;
      }

    });

    // =========================================================================
    // class catalog query
    // =========================================================================
    module.CatalogQuery.prototype = $.extend({}, module.Query.prototype, {

      /**
       * A catalog query.
       * @constructs sap.bc.ina.api.sina.impl.inav2.CatalogQuery
       * @augments {sap.bc.ina.api.sina.impl.inav2.sina_impl.Query}
       * @param  {Object} properties Configuration object.
       * @since SAP HANA SPS 06
       * @private
       * @ignore
       */
      init: function(properties) {
        properties = properties || {};
        this.resultSetClass = module.SearchResultSet;
      },

      createJsonRequest: function() {
        return jsontemplates.catalogRequest;
      }

    });



    // =========================================================================
    // class Facet
    // =========================================================================
    module.Facet.prototype = $.extend({}, sinabase.Facet.prototype, {

      /**
       * A facet that is contained in a perspective.
       * @ignore
       * @constructs sap.bc.ina.api.sina.impl.inav2.Facet
       */
      init: function(properties) {
        // TODO: make facet general available only in sinabase
        properties = properties || {};
        sinabase.Facet.prototype.init.apply(this, arguments);
        if (this.facetType === '$$datasources$$') {
          this.facetType = sinabase.FacetType.DATASOURCE;
          this.query = new module.ChartQuery();
          this.query.resultSet = new module.ChartResultSet({
            "type": "datasource"
          });
        } else if (this.facetType === sinabase.FacetType.ATTRIBUTE) {
          this.query = new module.ChartQuery();
          this.query.resultSet = new module.ChartResultSet();
        } else if (this.facetType === sinabase.FacetType.SEARCH) {
          this.query = new module.SearchQuery();
          this.query.resultSet = new module.SearchResultSet();
        }
      },

      // TODO: move out of facet into Perspective
      setJsonData: function(data) {
        this.query.resultSet.setJsonData(data);
        this._parseServerSideFacetMetaData(data.Metadata);
      },

      // TODO: move out of facet into Perspective
      _parseServerSideFacetMetaData: function(data) {
        if (data && data.Cube) {
          this.title = data.Cube.Description;
          this.dimension = data.Cube.ObjectName;
        }

      }

    });

    // =========================================================================
    // class perspective query
    // =========================================================================
    module.PerspectiveQuery.prototype = $.extend({}, sinabase.PerspectiveQuery.prototype, module.Query.prototype, {

      /**
       * Creates a perspective query.
       * @ignore
       * @constructs sap.bc.ina.api.sina.impl.inav2.PerspectiveQuery
       * @param  {Object} properties Configuration object.
       * @augments {sap.bc.ina.api.sina.impl.inav2.sina_impl.Query}
       */
      init: function(properties) {
        var self = this;
        self.chartFacets = [];
        self.searchFacet = null;
        self.searchresultset = null;
        self.layout = null;
        self.templateFactsheet = properties.templateFactsheet || false;
        properties = properties || {};
        module.Query.prototype.init.apply(this, arguments);
        sinabase.PerspectiveQuery.prototype.init.apply(this, arguments);
        properties.orderBy = properties.orderBy || {};
        this.setOrderBy(properties.orderBy);
        this.resultSetClass = module.Perspective;
      },

      createJsonRequest: function() {
        if (this.templateFactsheet) {
          jsontemplates.perspectiveRequest = jsontemplates.perspectiveRequestFactsheet;
        }
        jsontemplates.perspectiveRequest.DataSource = this.filter.dataSource.toInAJson();
        var searchterms = this.filter.getSearchTerms();
        jsontemplates.perspectiveRequest.Search.SearchTerms = searchterms;
        if (true || config || config.startWithSearch !== undefined || config.startWithSearch === 'false') {
          if (searchterms==="*" && this.filter.dataSource.getObjectName().value.toLowerCase() === '$$all$$') {
            //TODO: remove workaround: return a catalog request and only show datasource facet,
            //due to high response time while searching for '*' in $$ALL$$
            var cq = new module.CatalogQuery();
            return cq.createJsonRequest();
          }
        }
        jsontemplates.perspectiveRequest.Search.Top = this._top;
        jsontemplates.perspectiveRequest.Search.Skip = this._skip;
        jsontemplates.perspectiveRequest.Search.Filter = this.filter.getJson();
        jsontemplates.perspectiveRequest.Search.OrderBy = this._assembleOrderBy();
        return jsontemplates.perspectiveRequest;
      },

      getPerspective: function(onSuccess, onError) {
        this.getResultSet(onSuccess, onError);
      },

      generatePerspectiveSync: function() {
        return this.getResultSetSync();
      }

    });

    // =========================================================================
    // class Perspective (ResultSet)
    // =========================================================================
    module.Perspective.prototype = {

      /**
       *  A perspective.
       *  @constructs sap.bc.ina.api.sina.impl.inav2.Perspective
       *  @ignore
       */
      init: function(properties) {
        var self = this;
        self.chartFacets = [];
        self.searchFacet = null;
        self.searchresultset = null;
        self.layout = null;
        properties = properties || {};
      },

      getSearchResultSet: function() {
        return this.searchresultset;
      },

      getChartFacets: function() {
        return this.chartFacets;
      },

      getSearchFacet: function() {
        return this.searchFacet;
      },

      getLayout: function() {
        return this.layout;
      },

      determineFacetType : function(resultSetFacet){
          try{
              if(resultSetFacet.ResultSet.ItemLists[0].Items[0].NamedValues[0].Name==='$$DataSource$$')
                  return '$$datasources$$';
                else
                  return sinabase.FacetType.ATTRIBUTE;            
          } catch(e){
              return sinabase.FacetType.ATTRIBUTE;            
          }
      },
        
      setJsonData: function(data) {
        var self = this;
        //there is always a searchresult
        self.searchFacet = new module.Facet({
          "facetType": "searchresult"
        });
        self.searchFacet.setJsonData(data);
        self.searchresultset = self.searchFacet.getQuery().getResultSetSync();
        if (data.ResultsetFacets && data.ResultsetFacets.Elements) {
          for (var j = 0; j < data.ResultsetFacets.Elements.length; j++) {
            var facetType = self.determineFacetType(data.ResultsetFacets.Elements[j]); 
            var facet = new module.Facet({
              "facetType": facetType
            });
            facet.setJsonData(data.ResultsetFacets.Elements[j]);
            self.chartFacets.push(facet);
          }
        } else {
          //TODO: remove workaround
          //convert catalogquery searchresult to a datasource facet
          var dsFacet = new module.Facet({
            facetType: '$$datasources$$'
          });
          for (var i = 0; i < self.searchFacet.getQuery().getResultSetSync().getElements().length; i++) {
            var dataSource = new filter.DataSource();
            var resultElement = self.searchFacet.getQuery().getResultSetSync().getElements()[i];
            dataSource.setObjectName(resultElement.ObjectName.valueRaw);
            dataSource.setPackageName(resultElement.PackageName.valueRaw);
            dataSource.setSchemaName(resultElement.SchemaName.valueRaw);
            dataSource.setType(resultElement.Type.valueRaw);
            dataSource.setLabel(resultElement.Description.value);
            dsFacet.getQuery().getResultSetSync().getElements()[i] = {
              dataSource: dataSource
            };
          }
          self.searchFacet.getQuery().getResultSetSync().elements = [];
          self.searchFacet.getQuery().getResultSetSync().totalcount = -1;
          self.chartFacets.push(dsFacet);
        }

      }

    };

    // =========================================================================
    // class perspective query
    // =========================================================================
    module.PerspectiveGetQuery.prototype = $.extend({}, module.Query.prototype, {

      /**
       * Creates a perspective search query.
       * @ignore
       * @constructs sap.bc.ina.api.sina.impl.inav2.PerspectiveGetQuery
       * @param  {Object} properties Configuration object.
       * @augments {sap.bc.ina.api.sina.impl.inav2.sina_impl.Query}
       */
      init: function(properties) {
        var self = this;
        properties = properties || {};
        this.perspectiveId = properties.perspectiveId;
        module.Query.prototype.init.apply(this, [properties]);
        this.resultSetClass = module.Perspective2;
      },

      setPerspectiveId: function(perspectiveId) {
        this.perspectiveId = perspectiveId;
      },

      getPerspective: function(onSuccess, onError) {
        var self = this;
        var request = {
          async: true,
          url: "/sap/bc/ina/service/v2/Perspectives('" + this.perspectiveId + "')",
          type: "GET"
        };

        var jqXHR = this.system.proxy.ajax(request);

        jqXHR.done(function(data) {
          if (data && data.error) {
            onError(data.error);
          }
          self.resultSet = new self.resultSetClass(self.resultSetProperties);
          self.resultSet.setJsonData(data);
          if (onSuccess) {
            onSuccess(self.resultSet);
          }
        })
          .fail(function(error) {
            if (onError)
              onError(error);
          });

      }

    });

    // =========================================================================
    // class Perspective 2 - static JSON form server
    // =========================================================================
    module.Perspective2.prototype = {

      /**
       *  A perspective.
       *  @constructs sap.bc.ina.api.sina.impl.inav2.Perspective2
       *  @ignore
       */
      init: function(properties) {
        var self = this;
        properties = properties || {};
      },


      getFacetForID: function(facetId) {
        var self = this;
        for (var j = 0; j < self.facets.length; j++) {
          if (self.facets[j].facetId === facetId) {
            return self.facets[j];
          }
        }
        return undefined;

      },

      getDimensionForID: function(dimensionId) {
        var self = this;
        for (var j = 0; j < self.dimensions.length; j++) {
          if (self.dimensions[j].Name === dimensionId) {
            return self.dimensions[j];
          }
        }
        return undefined;

      },

      getPreviewDimension: function() {
        var self = this;
        if (this.bindings["WIDGET-4"] && this.bindings["WIDGET-4"].dimension) {
          return this.bindings["WIDGET-4"].dimension;
        }
        if (this.bindings["WIDGET-1"] && this.bindings["WIDGET-1"].dimension) {
          return this.bindings["WIDGET-1"].dimension;
        }
        if (this.bindings["WIDGET-2"] && this.bindings["WIDGET-2"].dimension) {
          return this.bindings["WIDGET-2"].dimension;
        }
        if (this.bindings["WIDGET-3"] && this.bindings["WIDGET-3"].dimension) {
          return this.bindings["WIDGET-3"].dimension;
        }
        return undefined;

      },

      setJsonData: function(data) {
        var self = this;
        self.rawdata = data;
        self.ChangedAt = data.ChangedAt;
        self.ChangedBy = data.ChangedBy;
        self.content = JSON.parse(data.Content);
        self.perspectiveId = data.Id;
        self.name = data.Name;
        self.packageName = data.Package;
        self.isActive = data.isActive;
        self.isGenerated = data.isGenerated;

        self.datasource = self.content.Model.Queries[0].Datasource;

        self.measures = [];
        var measuresJSON = self.content.Model.Queries[0].CustomDimension1.Members;
        for (var k = 0; k < measuresJSON.length; k++) {
          self.measures.push(measuresJSON[k]);
        }

        self.dimensions = [];
        var dimensionJSON = self.content.Model.Queries[0].Dimensions;
        for (var j = 0; j < dimensionJSON.length; j++) {
          self.dimensions.push(dimensionJSON[j]);
        }

        self.facets = [];
        var facetJSON = self.content.Model.Facets;
        for (var i = 0; i < facetJSON.length; i++) {

          self.facets.push({
            isActive: facetJSON[i].Active,
            facetId: facetJSON[i].FacetId,
            dimension: self.getDimensionForID(facetJSON[i].SeriesChart.ScaleDimensions[0].DimensionName)
          });


        }

        self.bindings = {};
        var bindingsJSON = self.content.Bindings;
        for (var l = 0; l < bindingsJSON.length; l++) {
          var bindingJSON = bindingsJSON[l];
          self.bindings[bindingJSON.WidgetId] = self.getFacetForID(bindingJSON.FacetId);
        }
      }

    };

    // =========================================================================
    // class perspective query
    // =========================================================================
    module.PerspectiveSearchQuery.prototype = $.extend({}, module.Query.prototype, {

      /**
       * Creates a perspective search query.
       * @ignore
       * @constructs sap.bc.ina.api.sina.impl.inav2.PerspectiveSearchQuery
       * @param  {Object} properties Configuration object.
       * @augments {sap.bc.ina.api.sina.impl.inav2.sina_impl.Query}
       */
      init: function(properties) {
        var self = this;
        properties = properties || {};
        module.Query.prototype.init.apply(this, [properties]);
        this.filter.searchTerms = properties.searchTerms || "*";
        this.resultSetClass = module.PerspectiveSearchResultSet;
      },


      createJsonRequest: function() {
        jsontemplates.perspectiveSearchRequest.Search.SearchTerms = this.filter.searchTerms;
        jsontemplates.perspectiveSearchRequest.Search.Top = this._top;
        jsontemplates.perspectiveSearchRequest.Search.Skip = this._skip;
        return jsontemplates.perspectiveSearchRequest;
      },

      getPerspectiveResults: function(onSuccess, onError) {
        this.getResultSet(onSuccess, onError);
      }

    });

    // =========================================================================
    // class PerspectiveSearch (ResultSet)
    // =========================================================================
    module.PerspectiveSearchResultSet.prototype = {

      /**
       *  A perspective.
       *  @constructs sap.bc.ina.api.sina.impl.inav2.sina_impl.PerspectiveSearchResultSet
       *  @ignore
       */
      init: function(properties) {
        var self = this;
        self.perspectives = [];
        properties = properties || {};
      },

      getPerspectiveSearchResultSet: function() {
        return this.perspectives;
      },

      setJsonData: function(data) {
        var self = this;

        var perspectivesJSON = data["ItemLists"][0]["Items"];
        for (var i = 0; i < perspectivesJSON.length; i++) {
          var namedValues = perspectivesJSON[i]["NamedValues"];
          var perspective = {
            packageId: namedValues[0].Value,
            perspectiveId: namedValues[0].Value + "/" + namedValues[1].Value,
            perspectiveDescription: namedValues[3].Value,
            title: namedValues[2].Value
          };
          self.perspectives.push(perspective);
        }

      }

    };

    // =========================================================================
    // class suggestion query
    // =========================================================================
    module.SuggestionQuery.prototype = $.extend({}, sinabase.SuggestionQuery.prototype, module.Query.prototype, {

      /**
       * A suggestion query for a SAP HANA system.
       * @constructs sap.bc.ina.api.sina.impl.inav2.sina_impl.SuggestionQuery
       * @augments {sap.bc.ina.api.sina.sinabase.SuggestionQuery}
       * @augments {sap.bc.ina.api.sina.impl.inav2.sina_impl.Query}
       * @param  {Object} properties Configuration object.
       * @since SAP HANA SPS 06
       * @private
       */
      init: function(properties) {
        properties = properties || {};
        this.attributes = properties.attributes || [];
        module.Query.prototype.init.apply(this, arguments);
        sinabase.SuggestionQuery.prototype.init.apply(this, arguments);
        this.resultSetClass = module.SuggestionResultSet;
      },

      /**
       * Adds a response attribute to this suggestion query. This attributes is used
       * to look for suitable suggestions for the search term of this query. At least one term is required.
       * @instance
       * @since SAP HANA SPS 06
       * @memberOf sap.bc.ina.api.sina.impl.inav2.sina_impl.SuggestionQuery
       * @param {String} attribute The name of the attribute as given in the SAP HANA database.
       */
      addResponseAttribute: function(attribute) {
        this.attributes.push(attribute);
        this._resetResultSet();
        return this;
      },

      /**
       * Sets a list of response attributes for this suggestion query. These attributes are used
       * to look for suitable suggestions for the search term of this query. At least one term is required.
       * @instance
       * @since SAP HANA SPS 06
       * @memberOf sap.bc.ina.api.sina.impl.inav2.sina_impl.SuggestionQuery
       * @param {Array} attributes A list of names of the attributes as given in the SAP HANA database.
       */
      setResponseAttributes: function(attributes) {
        this.attributes = attributes;
        this._resetResultSet();
        return this;
      },

      createJsonRequest: function() {
        jsontemplates.suggestionRequest.Suggestions.Precalculated = false;
        var searchterms = this.filter.getSearchTerms();
        if (this.attributes.length === 0) {
          throw "add at least one response attribute to your query";
        }
        jsontemplates.suggestionRequest.Suggestions.SearchTerms = searchterms;
        jsontemplates.suggestionRequest.Suggestions.AttributeNames = this.attributes;
        jsontemplates.suggestionRequest.DataSource = this.filter.dataSource.toInAJson();
        jsontemplates.suggestionRequest.Suggestions.Top = this._top;
        var filter = this.filter.getJson();
        if (filter && filter.Selection) {
          jsontemplates.suggestionRequest.Suggestions.Filter = this.filter.getJson();
        }
        return jsontemplates.suggestionRequest;
      }

    });


    // =========================================================================
    // class suggestion result set
    // =========================================================================
    module.SuggestionResultSet.prototype = {

      /**
       * A suggestion result set for a SAP HANA system.
       * @constructs sap.bc.ina.api.sina.impl.inav2.sina_impl.SuggestionResultSet
       * @since SAP HANA SPS 06
       * @private
       */
      init: function() {
        this.suggestions = [];
      },

      setJsonData: function(data) {
        this.suggestions = [];
        var itemLists = {};
        for (var i = 0; i < data.ItemLists.length; i++) {
          itemLists[data.ItemLists[i].Name] = data.ItemLists[i];
        }
        // only Suggestions ItemList is relevant here
        for (var a = 0; a < itemLists.Suggestions.Items.length; a++) {
          var item = itemLists.Suggestions.Items[a],
            term = "",
            attribute = "",
            attributeDescription = "";
          // dataSource = new filter.DataSource(),
          // dataSourceDescription = "";
          var suggestion = {};
          for (var d = 0; d < item.NamedValues.length; ++d) {
            var namedValue = item.NamedValues[d];
            switch (namedValue.Name) {
              case "Term":
                term = namedValue.Value;
                suggestion.label = term;
                break;
                // case "$$DataSource$$":
                //     dataSource = namedValue.Value;
                //     dataSource.setObjectName(dataSource);
                // break;
                // case "$$DataSourceDescription$$":
                //     dataSourceDescription = namedValue.Value;
                //     dataSource.setLabel(dataSourceDescription);
                // break;
              case "AttributeName":
                attribute = namedValue.Value;
                suggestion.attribute = attribute;
                break;
              case "$$AttributeDescription$$":
                attributeDescription = namedValue.Value;
                suggestion.attributeDescription = attributeDescription;
                break;
              case "Score":
                var score = namedValue.Value;
                suggestion.score = parseInt(score, 10);
                break;
            }

          }
          // suggestion.dataSource = dataSource;
          this.suggestions.push(suggestion);
        }
        this.suggestions.sort(function(a, b) {
          return b.score - a.score;
        });
        for (var o = this.suggestions.length - 1; o >= 0; o--) {
          delete this.suggestions[o].score;
        }
      },

      /**
       * Returns the elements of the result set, ordered by relevancy score.
       * @memberOf sap.bc.ina.api.sina.impl.inav2.sina_impl.SuggestionResultSet
       * @instance
       * @since SAP HANA SPS 06
       * @return {Array} A list of result set elements.
       * @example
       * var queryProperties = {
       *     dataSource  : { schemaName : "SYSTEM",
       *                     objectName : "J_EPM_PRODUCT"
       *     },
       *     searchTerms : "s*",
       *     attributes  : ['CATEGORY','PRODUCT_ID','TEXT','PRICE','CURRENCY_CODE']
       * };
       * var query = sap.bc.ina.api.sina.createSuggestionQuery(queryProperties);
       * var resultSet = query.getResultSetSync();
       * var elements = resultSet.getElements();
       * // contents of elements (shortened):
       * [{"label":"USD","attribute":"CURRENCY_CODE"},
       *   {"label":"Software","attribute":"CATEGORY"},
       *   {"label":"Scanner","attribute":"CATEGORY"},
       *   {"label":"Speakers","attribute":"CATEGORY"},
       *   {"label":"1200 dpi x 1200 dpi - up to 25 ppm (mono) / up to 24 ppm (colour) - capacity: 100 sheets - Hi-Speed USB2.0, Ethernet","attribute":"TEXT"},
       *   {"label":"1000 dpi x 1000 dpi - up to 16 ppm (mono) / up to 15 ppm (colour)- capacity 80 sheets - scanner (216 x 297 mm, 1200dpi x 2400dpi)","attribute":"TEXT"},
       *   {"label":"Print 2400 dpi image quality color documents at speeds of up to 32 ppmĀ¹ (color) or 36 ppmĀ¹ (monochrome), letter/A4. Powerful 500 MHz processor, 512MB of memory","attribute":"TEXT"},
       *   {"label":"Scanner and Printer","attribute":"CATEGORY"},
       *   {"label":"1000 dpi x 1000 dpi - up to 15 ppm (mono) / up to 13 ppm (colour) - capacity: 40 sheets - Hi-Speed USB - excellent dimesions for the small office","attribute":"TEXT"},
       *   {"label":"Print up to 25 ppm letter and 24 ppm A4 color or monochrome, with a first-page-out-time of less than 13 seconds for monochrome and less than 15 seconds for color","attribute":"TEXT"}
       *  ]
       */
      getElements: function() {
        return this.suggestions;
      }

    };

    // =========================================================================
    // class suggestion 2 query
    // =========================================================================
    module.Suggestion2Query.prototype = $.extend({}, sinabase.SuggestionQuery.prototype, module.Query.prototype, {

      init: function(properties) {
        properties = properties || {};
        this.attributes = properties.attributes || [];
        module.Query.prototype.init.apply(this, [properties]);
        sinabase.SuggestionQuery.prototype.init.apply(this, [properties]);
        this.resultSetClass = module.Suggestion2ResultSet;
        this.searchTerms = properties.searchTerms || {};
        this.suggestionTerm = properties.suggestionTerm || '';
      },

      /**
       * Adds a response attribute to this suggestion query. Instead of the SuggestionQuery
       * which searches within these attributes. This Suggestion2Query will search everywhere
       * but only returns these response attributes.
       * @ignore
       * @instance
       * @since SAP HANA SPS 06
       * @memberOf sap.bc.ina.api.sina.impl.inav2.Suggestion2Query
       * @param {String} attribute the name of the attribute as given in the HANA database
       */
      addResponseAttribute: function(attribute) {
        this.attributes.push(attribute);
        this._resetResultSet();
        return this;
      },


      addSearchTerm: function(term) {
        if (term) {
          this.searchTerms[term] = term;
        }
        this._resetResultSet();
        return this;
      },

      setSuggestionTerm: function(term) {
        if (term.charAt(term.length - 1) !== '*') {
          term += '*';
        }
        if (this.suggestionTerm !== term) {
          this.suggestionTerm = term;
          this._resetResultSet();
        }
        return this;
      },


      /**
       * Adds a response attribute to this suggestion query. Instead of the SuggestionQuery
       * which searches within these attributes. This Suggestion2Query will search everywhere
       * but only returns these response attributes.
       * @ignore
       * @instance
       * @memberOf sap.bc.ina.api.sina.impl.inav2.Suggestion2Query
       * @param {Array} attributes list with the names of the attribute as given in the SAP HANA database.
       */
      setResponseAttributes: function(attributes) {
        this.attributes = attributes;
        this._resetResultSet();
        return this;
      },

      createJsonRequest: function() {
        jsontemplates.suggestion2Request.Suggestions2.Precalculated = false;
        jsontemplates.suggestion2Request.Suggestions2.NamedValues = [];
        for (var i = 0; i < this.attributes.length; i++) {
          jsontemplates.suggestion2Request.Suggestions2.NamedValues.push({
            AttributeName: this.attributes[i],
            Name: this.attributes[i]
          });
        }
        jsontemplates.suggestion2Request.DataSource = this.filter.dataSource.toInAJson();
        jsontemplates.suggestion2Request.Suggestions2.Top = this.getTop();
        jsontemplates.suggestion2Request.Suggestions2.Skip = this.getSkip();
        var rootConditionGroup = new filter.ConditionGroup();
        var searchTermConditions = new filter.ConditionGroup();
        for (var searchTerm in this.searchTerms) {
          var stCondition = new filter.Condition('$$SearchTerms$$', 'contains', searchTerm);
          rootConditionGroup.addCondition(stCondition);
        }
        var suggestionCondition = new filter.Condition('$$SuggestionTerms$$', 'contains', this.suggestionTerm);
        rootConditionGroup.addCondition(suggestionCondition);
        rootConditionGroup.addCondition(this.getFilter().getFilterConditions());
        jsontemplates.suggestion2Request.Suggestions2.Filter = rootConditionGroup.getJson();
        return jsontemplates.suggestion2Request;
      }

    });

    // =========================================================================
    // class suggestion 2 result set
    // =========================================================================
    module.Suggestion2ResultSet.prototype = {

      init: function() {
        this.datasources = {};
        this.suggestions = [];
      },

      setJsonData: function(data) {
        this.datasources = {};
        this.suggestions = [];

        function isNamedValueSuitable(name) {
          switch (name) {
            //upper and lower bounds are not needed now
            case "Value1":
            case "Value2":
            case "Order":
              return false;
            default:
              return true;
          }
        }

        var itemLists = {};
        if (!data.ItemLists) {
          return;
        }
        for (var i = 0; i < data.ItemLists.length; i++) {
          itemLists[data.ItemLists[i].Name] = data.ItemLists[i];
        }

        for (var a = 0; a < data.Grids.length; a++) {
          var axes = data.Grids[a].Axes;
          var cells = data.Grids[a].Cells;
          if (axes === undefined || cells === undefined) {
            return;
          }
          var suggestionDict = {};
          for (var cellIndex = 0; cellIndex < cells.length; ++cellIndex) {
            var cell = cells[cellIndex],
              attributeName = "",
              attributeLabel = "",
              dataSourceLabel = "",
              suggestion = {};
            suggestion.dataSource = new filter.DataSource();
            suggestion.attribute = {};
            for (var j = 0; j < cell.Index.length; j++) {
              var cellIndexValue = cell.Index[j];
              var tuple = axes[j].Tuples[cellIndexValue];
              if (tuple === undefined) {
                continue;
              }
              for (var c = 0; c < tuple.length; c++) {
                var dimension = axes[j].Dimensions[c];
                var tupleValueForDimension = tuple[c];
                var itemlist = itemLists[dimension.ItemListName];
                var namedValues = itemlist.Items[tupleValueForDimension].NamedValues;
                for (var d = 0; d < namedValues.length; ++d) {
                  var namedValue = namedValues[d];
                  switch (namedValue.Name) {
                    case "$$Term$$":
                      suggestion.valueRaw = cell.Value || cell.ValueFormatted || null;
                      suggestion.value = cell.ValueFormatted || cell.Value || null;
                      suggestion.labelRaw = namedValue.Value;
                      suggestion.label = namedValue.ValueFormatted;
                      if (!suggestion.filter) {
                        suggestion.filter = {};
                      }
                      suggestion.filter.value = suggestion.labelRaw;
                      suggestion.filter.valueLabel = suggestion.labelRaw;

                      break;
                    case "$$DataSource$$":
                      var objectName = namedValue.Value;
                      suggestion.dataSource.setObjectNameValue(objectName);
                      break;
                    case "$$DataSourceDescription$$":
                      var objectNameLabel = namedValue.Value;
                      suggestion.dataSource.setObjectNameLabel(objectNameLabel);
                      break;
                    case "$$Attribute$$":
                      var attribute = namedValue.Value;
                      if (!suggestion.filter) {
                        suggestion.filter = {};
                      }
                      suggestion.filter.attribute = attribute;
                      suggestion.attribute.value = attribute;
                      break;
                    case "$$AttributeDescription$$":
                      attributeLabel = namedValue.Value;
                      if (!suggestion.filter) {
                        suggestion.filter = {};
                      }
                      suggestion.filter.attributeLabel = attributeLabel;
                      suggestion.attribute.label = attributeLabel;
                      break;
                  }

                }
              }
            }
            this.suggestions.push(suggestion);
          }

        }

      },


      getElements: function() {
        return this.suggestions;
      }

    };

    // =========================================================================
    // class chart query
    // =========================================================================
    module.ChartQuery.prototype = $.extend({}, sinabase.ChartQuery.prototype, module.Query.prototype, {

      /**
       * A query that yields results suitable for simple chart controls, like
       * pie or bar charts.
       * @since SAP HANA SPS 06
       * @constructs sap.bc.ina.api.sina.impl.inav2.sina_impl.ChartQuery
       * @augments {sap.bc.ina.api.sina.impl.inav2.sina_impl.Query}
       * @augments {sap.bc.ina.api.sina.sinabase.ChartQuery}
       * @private
       */
      init: function(properties) {
        properties = properties || {};
        module.Query.prototype.init.apply(this, [properties]);
        sinabase.ChartQuery.prototype.init.apply(this, [properties]);
        this.dimensions = {};
        this.dimensions.CustomDimension1 = {
          Axis: "Columns",
          Name: "CustomDimension1",
          Members: []
        };
        properties.dimensions = properties.dimensions || {};
        if ($.type(properties.dimensions) === 'array') {
          for (var i = 0; i < properties.dimensions.length; i++) {
            this.addDimension(properties.dimensions[i]);
          }
        } else if ($.type(properties.dimensions) === 'string' || $.type(properties.dimensions) === 'object') {
          this.addDimension(properties.dimensions);
        }
        properties.measures = properties.measures || {};
        if ($.type(properties.measures) === 'array') {
          for (var j = 0; j < properties.measures.length; j++) {
            this.addMeasure(properties.measures[j]);
          }
        } else if ($.type(properties.measures) === 'object') {
          this.addMeasure(properties.measures);
        }
        this.resultSetClass = module.ChartResultSet;
      },

      createJsonRequest: function() {
        jsontemplates.chartRequest.DataSource = this.filter.dataSource.toInAJson();
        jsontemplates.chartRequest.SearchTerms = this.filter.getSearchTerms();
        jsontemplates.chartRequest.Analytics.Definition.Filter = this.filter.getJson();
        jsontemplates.chartRequest.Analytics.Definition.Dimensions = [];
        for (var dimension in this.dimensions) {
          jsontemplates.chartRequest.Analytics.Definition.Dimensions.push(this.dimensions[dimension]);
        }
        return jsontemplates.chartRequest;
      },

      /**
       * Adds a count measure for the given dimension to the chart query.
       * @memberOf sap.bc.ina.api.sina.impl.inav2.ChartQuery
       * @instance
       * @param  {string} dimension The dimension that the count is computed for.
       * @return {sap.bc.ina.api.sina.impl.inav2.ChartQuery} The chart query to allow chained method calls.
       */
      count: function(dimension) {
        this.addMeasure({
          name: dimension,
          aggregationFunction: 'COUNT'
        });
        return this;
      },

      /**
       * Adds one of the following aggregations to a dimension: 'COUNT', 'AVG', 'SUM', 'MIN', 'MAX'
       * @memberOf sap.bc.ina.api.sina.impl.inav2.ChartQuery
       * @instance
       * @since SAP HANA SPS 06
       * @param {Object} properties The configuration object can have the following properties:p
       * name, aggregationFunction, sortOrder, top.
       * @example
       * var query = sina.createChartQuery()
       *  .dataSource({ "schemaName"  : {"value":"SYSTEM"},
       *                "objectName"  : {"value":"J_EPM_PRODUCT"})
       * .addDimension("CATEGORY")
       * .addMeasure({name:"CATEGORY",aggregationFunction:"COUNT"});
       * var resultSet = query.getResultSetSync();
       */
      addMeasure: function(properties) {
        if ($.isEmptyObject(properties)) {
          return {};
        }
        var axis = "Columns";
        var name = "CustomDimension1"; //COUNT_"+dimension;
        var member;
        if (properties.aggregationFunction.toUpperCase() === 'COUNT' || properties.aggregationFunction.toUpperCase() === 'AVG') {
          member = this._createAggregationDimension(properties.aggregationFunction, properties.name, properties.aggregationFunction, properties.sortOrder || undefined, properties.top || undefined);
        } else {
          member = this._createAggregationDimension(properties.aggregationFunction, "", properties.name, properties.sortOrder, properties.top);
          delete member.AggregationDimension;
          delete member.Name;
          delete member.SortOrder;
        }
        this.dimensions.CustomDimension1.Members.push(member);
        return this;
      },

      /**
       * Adds a dimension to the query.
       * @memberOf sap.bc.ina.api.sina.impl.inav2.ChartQuery
       * @instance
       * @since SAP HANA SPS 06
       * @param {String|Object} dimension The name of the dimension. This is the same as the name of the corresponding database attribute.
       * If it is an object, the name and values of this object must be the same as the functions parameters.
       * @param {int} sortOrder Sort order of this dimension. 1 for ascending, 2 for descending.
       * Default is 1.
       * @param {int} top How many members does the dimension have? The default value is 5.
       * @example <caption>Plain function call</caption>
       * var query = sina.createChartQuery()
       * .addDimension("CATEGORY",1,5)
       * @example <caption>Call with properties object</caption>
       * var query = sina.createChartQuery({
       *     dimensions: [{name: "YEAR", sortOrder: 1, top:5}]
       * });
       */
      addDimension: function(dimension, sortOrder, top) {
        if ($.type(dimension) === 'object') {
          if ($.isEmptyObject(dimension)) {
            return {};
          }
          this.dimensions[dimension.name] = this._createDimension(dimension.name, null, dimension.sortOrder, dimension.top);
        } else if ($.type(dimension) === 'string') {
          if (!dimension) {
            return {};
          }
          this.dimensions[dimension] = this._createDimension(dimension, null, sortOrder, top);
        }
        return this;
      },

      _createDimension: function(dimension, axis, sortOrder, top) {
        return {
          "Axis": axis || "Rows",
          "Name": dimension,
          "SortOrder": sortOrder || 1,
          "Top": top || 5
        };
      },

      /**
       * Creates an aggregation for a dimension.
       * @private
       * @ignore
       * @param  {String} aggregation Type of aggregation to be created. The default value is SUM.
       * @param  {String} dimension   Dimension that the aggregation is created for.
       * @param  {int}    sortOrder   Sort order of the aggregation dimension. 1 for ascending, 2 for descending.
       * @param  {String} name        Name of the aggregation to be created.
       * @return {Object}             An object suitable for an INA request.
       */
      _createAggregationDimension: function(aggregationFunction, aggregationDimension, name, sortOrder) {
        return {
          "Aggregation": aggregationFunction,
          "AggregationDimension": aggregationDimension,
          "MemberOperand": {
            "AttributeName": "Measures",
            "Comparison": "=",
            "Value": name
          },
          "Name": name,
          "SortOrder": sortOrder || 2
        };
      }

    });

    // =========================================================================
    // class chart result set
    // =========================================================================
    module.ChartResultSet.prototype = {

      /**
       * A result set that yields elements suitable for simple chart controls, like
       * pie or bar charts.
       * @since SAP HANA SPS 06
       * @constructs sap.bc.ina.api.sina.impl.inav2.sina_impl.ChartResultSet
       * @private
       */
      init: function(properties) {
        properties = properties || {};
        this.type = properties.type;
        this.elements = [];
      },

      /**
       * Creates filter conditions for simple charts based on named values.
       * It also decides whether the element sets a filter range or an 'equals' filter.
       * There are the following cases:
       * 1) Value1 and Value2 both have values that are not the empty string:
       * It is a range with upper and lower boundary.
       * 2) Value2 has no value:
       * It is not a range but an equals ("=") filter condition.
       * 3) Either Value1 or Value2 have an empty string ("") as value:
       * It is a range with only one boundary, upper or lower.
       * @private
       * @ignore
       * @param  {object} element     The chart element that the filter condition is added to.
       * @param  {object} namedValues Server side data.
       * @param  {object} metadata    Server side meta data.
       */
      _parseNamedValues: function(element, namedValues, metadata) {
        var self = this;

        for (var i = 0; i < namedValues.length; i++) {
          var name = namedValues[i].Name;
          var value = namedValues[i].Value;
          element.label = namedValues[i].ValueFormatted;
          element.labelRaw = namedValues[i].Value;
          switch (name) {
            case '$$DataSource$$':
              self._parseNamedValuesForDataSource(element, value);
              break;
            case '$$AttributeValue$$':
              self._parseNamedValuesForRange(element, value, metadata);
              break;
          }

        }
      },

      _parseNamedValuesForRange: function(element, values, metadata) {
        var valueIDRaw,
          value1,
          value2;

        for (var d = 0; d < values.length; ++d) {
          var namedValue = values[d];
          switch (namedValue.Name) {
            case "ValueID":
              element.label = namedValue.ValueFormatted;
              valueIDRaw = namedValue.Value;
              break;
            case "Value1":
              if (metadata.Cube.ObjectName && (namedValue.Value !== undefined)) {
                value1 = new filter.Condition(metadata.Cube.ObjectName, ">=", namedValue.Value);
              }
              break;
            case "Value2":
              if (metadata.Cube.ObjectName && (namedValue.Value !== undefined)) {
                value2 = new filter.Condition(metadata.Cube.ObjectName, "<=", namedValue.Value);
              }
              break;
            case "Order":
              break;
          }
        }
        if (valueIDRaw) {
          if (value1 && value2) {
            // 1) range
            var group = new filter.ConditionGroup();
            group.setOperator("AND");
            group.setLabel(element.label);
            if (value1.value) {
              // 3) upper boundary of the range
              group.addCondition(value1);
            }
            if (value2.value) {
              // 3) lower boundary of the range
              group.addCondition(value2);
            }
            element.labelRaw = group;
          } else if (value1 && value1.value && (!value2)) {
            // 2) not a range
            value1.operator = '=';
            element.labelRaw = value1;
          } else if (value2 && value2.value && (!value1)) {
            // 2) not a range
            value2.operator = '=';
            element.labelRaw = value2;
          }
        }
      },

      // DataSource chart (category tree)
      _parseNamedValuesForDataSource: function(element, values) {

        element.dataSource = new filter.DataSource();
        for (var d = 0; d < values.length; ++d) {
          var namedValue = values[d];
          switch (namedValue.Name) {
            case "ObjectName":
              var label = namedValue.ValueFormatted;
              element.dataSource.setLabel(label);
              element.dataSource.setObjectName(namedValue.Value, namedValue.ValueFormatted);
              break;
            case "PackageName":
              element.dataSource.setPackageName(namedValue.Value, namedValue.ValueFormatted);
              break;
            case "SchemaName":
              element.dataSource.setSchemaName(namedValue.Value, namedValue.ValueFormatted);
              break;
            case "Type":
              element.dataSource.setType(namedValue.Value, namedValue.ValueFormatted);
              break;
            default:
              element[namedValue.Name] = namedValue.Value;
              break;
          }
        }
      },

      setJsonData: function(data) {
        this.elements = [];
        var metadata;
        if (data.Metadata) {
          metadata = data.Metadata;
        }
        if (data.ResultSet) {
          data = data.ResultSet;
        }
        var itemLists = {};
        for (var i = 0; i < data.ItemLists.length; i++) {
          itemLists[data.ItemLists[i].Name] = data.ItemLists[i];
        }

        for (var a = 0; a < data.Grids.length; a++) {
          var axes = data.Grids[a].Axes;
          var cells = data.Grids[a].Cells;
          for (var cellIndex = 0; cellIndex < cells.length; ++cellIndex) {
            //one dimension chart
            if (axes[0].Dimensions.length === 1) {
              var cell = cells[cellIndex];
              var element = {
                valueRaw: cell.Value || cell.ValueFormatted || null,
                value: cell.ValueFormatted || cell.Value || null
              };
              // only axes 0 is relevant for chart results
              for (var j = 0; j < 1; j++) {
                var cellIndexValue = cell.Index[j];
                var tuple = axes[j].Tuples[cellIndexValue];
                if (tuple === undefined) {
                  continue;
                }
                for (var c = 0; c < tuple.length; c++) {
                  var dimension = axes[j].Dimensions[c];
                  var tupleValueForDimension = tuple[c];
                  var itemlist = itemLists[dimension.ItemListName];
                  var namedValues = itemlist.Items[tupleValueForDimension].NamedValues;
                  this._parseNamedValues(element, namedValues, metadata);
                }
              }
              this.elements.push(element);
            }

          }
        }

      },

      /**
             * Returns the elements of the result set.
             * @memberOf sap.bc.ina.api.sina.impl.inav2.sina_impl.ChartResultSet
             * @instance
             * @since SAP HANA SPS 06
             * @return {Array} A list of result set elements.
             * @example
             * var query = sap.bc.ina.api.sina.createChartQuery()
             * .dataSource({ schemaName : "SYSTEM",
             *               objectName : "J_EPM_PRODUCT"
             *  })
             * .addDimension("CATEGORY")
             * .addMeasure({ name : "CATEGORY",
             *               aggregationFunction : "COUNT"
             * }); //end of query
             * var resultSet = query.getResultSetSync();
             * var elements = resultSet.getElements();
             * // contents of elements:
             * [
                {
                  "label": "Others",
                  "labelRaw": "Others",
                  "value": "13",
                  "valueRaw": 13
                },
                {
                  "label": "Notebooks",
                  "labelRaw": "Notebooks",
                  "value": "10",
                  "valueRaw": 10
                },
                {
                  "label": "Flat screens",
                  "labelRaw": "Flat screens",
                  "value": "9",
                  "valueRaw": 9
                },
                {
                  "label": "Software",
                  "labelRaw": "Software",
                  "value": "8",
                  "valueRaw": 8
                },
                {
                  "label": "Electronics",
                  "labelRaw": "Electronics",
                  "value": "5",
                  "valueRaw": 5
                }
              ]
             */
      getElements: function() {
        return this.elements;
      }
    };

    // =======================================================================
    // result set parser
    // =======================================================================

    var ResultSetParser = function() {
      this.init.apply(this, arguments);
    };

    ResultSetParser.prototype = {

      init: function(options) {
        this.resultSet = options.resultSet;
        this.parse();
      },

      parse: function() {

        // enhance result set:
        // -> create link to item lists in dimensions of axes
        this.enhance(this.resultSet);

        // get reference to grid,row axis,col axis
        var grid = this.resultSet.Grids[0];
        var rowAxis = grid.Axes[0];
        var colAxis = grid.Axes[1];

        // key function for getting key of an item
        // (key needed for insertion into tree)
        var keyFunction = function(item) {
          return item.NamedValues[0].Value;
        };

        // create new tree
        var tree = new Tree({
          keyFunction: keyFunction
        });

        // loop at all cells and add cell to result tree
        for (var i = 0; i < grid.Cells.length; ++i) {

          var cell = grid.Cells[i];

          var rowIndex = cell.Index[0];
          var rowItems = this.resolve(rowAxis, rowIndex);
          var rowItemsDebug = rowItems.map(keyFunction);

          var colIndex = cell.Index[1];
          var colItems = this.resolve(colAxis, colIndex);
          var colItemsDebug = colItems.map(keyFunction);
          colItems = [$.extend({}, colItems[0])];
          // add cell info to col item
          colItems[0].cell = cell;

          // assemble tree path = rowItems + colItems
          var treePath = rowItems;
          treePath.push.apply(treePath, colItems);

          // insert
          tree.insert(treePath);

        }

        return tree;
      },

      resolve: function(axis, index) {
        var items = [];
        var tuples = axis.Tuples[index];
        for (var i = 0; i < tuples.length; ++i) {
          var itemIndex = tuples[i];
          var item = axis.Dimensions[i].ItemList.Items[itemIndex];
          items.push(item);
        }
        return items;
      },

      enhance: function(resultSet) {

        // create dictionary with item lists
        var itemListByName = {};
        for (var i = 0; i < resultSet.ItemLists.length; ++i) {
          var itemList = resultSet.ItemLists[i];
          itemListByName[itemList.Name] = itemList;
        }

        // loop at all dimensions and set link to item list
        for (i = 0; i < resultSet.Grids.length; ++i) {
          var grid = resultSet.Grids[i];
          for (var j = 0; j < grid.Axes.length; ++j) {
            var axis = grid.Axes[j];
            for (var k = 0; k < axis.Dimensions.length; ++k) {
              var dimension = axis.Dimensions[k];
              dimension.ItemList = itemListByName[dimension.ItemListName];
            }
          }
        }

      }

    };

    // =======================================================================
    // tree
    // =======================================================================

    var Tree = function() {
      this.init.apply(this, arguments);
    };

    Tree.prototype = {

      init: function(options) {

        // create root tree element
        this.root = {
          data: "root",
          subTree: {}
        };

        // set key function
        if (options && options.keyFunction) {
          this.keyFunction = options.keyFunction;
        } else {
          this.keyFunction = function(obj) {
            return obj;
          };
        }
      },

      insert: function(path) {
        var parent = this.root;
        for (var i = 0; i < path.length; ++i) {
          var pathElement = path[i];
          var key = this.keyFunction(pathElement);
          if (!parent.subTree.hasOwnProperty(key)) {
            parent.subTree[key] = {
              subTree: {},
              data: pathElement
            };
          }
          parent = parent.subTree[key];
        }
      },

      toString: function() {
        var stringStream = [];
        this.toStringHelper(this.root, [], stringStream);
        return stringStream.join("");
      },

      toStringHelper: function(tree, path, stringStream) {

        var pathElement = null;
        if (tree === this.root) {
          pathElement = tree.data;
        } else {
          pathElement = this.keyFunction(tree.data);
        }
        path.push(pathElement);


        var hasChildren = false;
        for (var child in tree.subTree) {
          if (tree.subTree.hasOwnProperty(child)) {
            hasChildren = true;
            var subTree = tree.subTree[child];
            var pathCopy = path.slice(0);
            this.toStringHelper(subTree, pathCopy, stringStream);
          }
        }

        if (!hasChildren) {
          stringStream.push(path.toString() + "\n");
        }
      }
    };

    // =========================================================================
    // class group bar chart query
    // =========================================================================
    module.GroupBarChartQuery.prototype = $.extend({}, module.ChartQuery.prototype, {

      /**
       * A query that yields results suitable for a grouped bar chart control.
       * @constructs sap.bc.ina.api.sina.impl.inav2.sina_impl.GroupBarChartQuery
       *  @augments {sap.bc.ina.api.sina.impl.inav2.sina_impl.ChartQuery}
       *  @private
       */
      init: function(properties) {
        properties = properties || {};
        module.ChartQuery.prototype.init.apply(this, [properties]);
        this.resultSetClass = module.GroupBarChartResultSet;
      }
    });

    // =========================================================================
    // class goruped bar chart result set
    // =========================================================================
    module.GroupBarChartResultSet.prototype = {

      /**
       * A result set that yields elements suitable for a grouped bar chart.
       * @since SAP HANA SPS 06
       * @constructs sap.bc.ina.api.sina.impl.inav2.sina_impl.GroupBarChartResultSet
       * @private
       */
      init: function(properties) {
        properties = properties || {};
        this.elements = [];
      },

      setJsonData: function(data) {
        var self = this;
        this.elements = [];
        var resultSetParser = new ResultSetParser({
          resultSet: data
        });
        var tree = resultSetParser.parse();

        function parseSubTree(subTree, parentElem) {
          for (var itemName in subTree) {
            var item = subTree[itemName];
            var elem = {
              label: item.data.NamedValues[0].ValueFormatted,
              value: []
            };
            if (item.data.cell) {
              elem.value = {
                value: item.data.cell.ValueFormatted,
                valueRaw: item.data.cell.Value
              };
            }
            if (parentElem) {
              parentElem.value.push(elem);
            } else {
              self.elements.push(elem);
            }
            if (item.subTree) {
              parseSubTree(item.subTree, elem);
            }
          }
        }
        parseSubTree(tree.root.subTree);

      },

      /**
             * Returns the elements of the result set.
             * @memberOf sap.bc.ina.api.sina.impl.inav2.sina_impl.GroupBarChartResultSet
             * @instance
             * @since SAP HANA SPS 06
             * @return {Array} A list of result set elements.
             * @example
             * var query = sap.bc.ina.api.sina.createGroupBarChartQuery();
             * query.dataSource({ schemaName : "SYSTEM",
             *                    objectName : "J_EPM_PRODUCT"
             * });
             * query.addDimension('CURRENCY_CODE');
             * query.addDimension('CATEGORY');
             * query.count('PRODUCT_ID');
             * var resultSet = query.getResultSetSync();
             * var elements = resultSet.getELements();
             * // contents of elements:
             * [
                {
                  "label": "EUR",
                  "value": [
                    {
                      "label": "Notebooks",
                      "value": [
                        {
                          "label": "COUNT",
                          "value": {
                            "value": "6",
                            "valueRaw": 6
                          }
                        }
                      ]
                    },
                    {
                      "label": "Others",
                      "value": [
                        {
                          "label": "COUNT",
                          "value": {
                            "value": "5",
                            "valueRaw": 5
                          }
                        }
                      ]
                    },
                    {
                      "label": "Software",
                      "value": [
                        {
                          "label": "COUNT",
                          "value": {
                            "value": "3",
                            "valueRaw": 3
                          }
                        }
                      ]
                    }
                  ]
                }
                ]
             */
      getElements: function() {
        return this.elements;
      }
    };

    // =========================================================================
    // class line chart query
    // =========================================================================
    module.LineChartQuery.prototype = $.extend({}, module.ChartQuery.prototype, {

      /**
       * A query that yields results suitable for a line chart control.
       * @constructs sap.bc.ina.api.sina.impl.inav2.sina_impl.LineChartQuery
       *  @augments {sap.bc.ina.api.sina.impl.inav2.sina_impl.ChartQuery}
       *  @private
       */
      init: function(properties) {
        properties = properties || {};
        module.ChartQuery.prototype.init.apply(this, [properties]);
        this.resultSetClass = module.LineChartResultSet;
      }
    });

    // =========================================================================
    // class line chart result set
    // =========================================================================
    module.LineChartResultSet.prototype = $.extend({}, module.GroupBarChartResultSet.prototype, {

      /**
       * A result set that yields elements suitable for a line chart.
       * @since SAP HANA SPS 06
       * @constructs sap.bc.ina.api.sina.impl.inav2.sina_impl.LineChartResultSet
       * @augments {sap.bc.ina.api.sina.impl.inav2.sina_impl.GroupBarChartResultSet}
       * @private
       */
      init: function(properties) {
        properties = properties || {};
        this.elements = [];
      },

      setJsonData: function(data) {
        var self = this;
        this.elements = [];
        var resultSetParser = new ResultSetParser({
          resultSet: data
        });
        var tree = resultSetParser.parse();

        //create line chart result format
        function parseSubTree(subTree, parentElem) {
          for (var dimensionLineItemName in subTree) {
            var dimensionLineItem = subTree[dimensionLineItemName];
            var elem = {
              label: dimensionLineItem.data.NamedValues[0].ValueFormatted,
              value: []
            };
            for (var dimensionXItemName in dimensionLineItem.subTree) {
              var dimensionXItem = dimensionLineItem.subTree[dimensionXItemName];
              var point = {
                x: dimensionXItem.data.NamedValues[0].ValueFormatted
              };
              for (var measureYItemName in dimensionXItem.subTree) {
                var measureYItem = dimensionXItem.subTree[measureYItemName];
                point.y = measureYItem.data.cell.Value;
              }
              elem.value.push(point);
            }
            self.elements.push(elem);
          }
        }
        parseSubTree(tree.root.subTree);
      },

      /**
             * Returns the elements of the result set.
             * @memberOf sap.bc.ina.api.sina.impl.inav2.sina_impl.LineChartResultSet
             * @instance
             * @since SAP HANA SPS 06
             * @return {Array} A list of result set elements.
             * @example
             * var queryProperties = {
             *     dataSource      : { schemaName  : "SYSTEM",
                                       objectName  : "J_EPM_PRODUCT"
                                     },
                   dimensionX      : {name: 'CATEGORY'},
                   dimensionLine   : {name: 'CURRENCY_CODE'},
                   measureY        : {name: 'PRODUCT_ID', aggregationFunction: 'COUNT'}
               };
               query = sap.bc.ina.api.sina.createLineChartQuery(queryProperties);
             * var resultSet = query.getResultSetSync();
             * var elements = resultSet.getEements();
             * // contents of elements (shortened):
             * [
                  {
                    "label": "EUR",
                    "value": [
                      {
                        "x": "Notebooks",
                        "y": 6
                      },
                      {
                        "x": "Others",
                        "y": 5
                      },
                      {
                        "x": "Software",
                        "y": 3
                      },
                      {
                        "x": "Speakers",
                        "y": 3
                      },
                      {
                        "x": "Electronics",
                        "y": 2
                      },
                      {
                        "x": "Flat screens",
                        "y": 2
                      },
                      {
                        "x": "Laser printers",
                        "y": 2
                      },
                      {
                        "x": "Mice",
                        "y": 2
                      },
                      {
                        "x": "PC",
                        "y": 2
                      },
                      {
                        "x": "Workstation ensemble",
                        "y": 2
                      }
                    ]
                  },
                  {
                    "label": "USD",
                    "value": [
                      {
                        "x": "Others",
                        "y": 4
                      },
                      {
                        "x": "Flat screens",
                        "y": 2
                      },
                      {
                        "x": "Handhelds",
                        "y": 2
                      },
                      {
                        "x": "High Tech",
                        "y": 2
                      },
                      {
                        "x": "Notebooks",
                        "y": 2
                      },
                      {
                        "x": "Software",
                        "y": 2
                      },
                      {
                        "x": "Electronics",
                        "y": 1
                      },
                      {
                        "x": "Graphic cards",
                        "y": 1
                      },
                      {
                        "x": "Handheld",
                        "y": 1
                      },
                      {
                        "x": "Headset",
                        "y": 1
                      }
                    ]
                  }
               ]
             */
      getElements: function() {
        return this.elements;
      }
    });

    // =========================================================================
    // class search query
    // =========================================================================
    module.SearchQuery.prototype = $.extend({}, sinabase.SearchQuery.prototype, module.Query.prototype, {

      /**
       * A query that yields results suitable for a simple result list.
       * @constructs sap.bc.ina.api.sina.impl.inav2.sina_impl.SearchQuery
       * @augments {sap.bc.ina.api.sina.sinabase.SearchQuery}
       * @augments {sap.bc.ina.api.sina.impl.inav2.sina_impl.Query}
       * @private
       */
      init: function(properties) {
        properties = properties || {};
        module.Query.prototype.init.apply(this, [properties]);
        sinabase.SearchQuery.prototype.init.apply(this, [properties]);
        this.sqlSearch = (properties.sqlSearch === undefined || properties.sqlSearch === true) ? true : false;
        this.attributes = properties.attributes || [];
        properties.orderBy = properties.orderBy || {};
        this.setOrderBy(properties.orderBy);
        this.resultSetClass = module.SearchResultSet;
      },

      /**
       * Adds a response attribute to the search query. The content of this
       * attribute is returned if the search term was found in one of the
       * response attributes.
       * @memberOf sap.bc.ina.api.sina.impl.inav2.sina_impl.SearchQuery
       * @instance
       * @param {String|Object} attribute If the argument is a string, it is
       * the name of an attribute of the database view. If it is an object, it can contain
       * the name of the attribute and additional server-side
       * functions, like snippet or highlighting.
       * See {@link sap.bc.ina.api.sina.sinabase.Sina#createSearchQuery} for examples.
       */
      addResponseAttribute: function(attribute) {
        this.attributes.push(attribute);
        this._resetResultSet();
        return this;
      },

      createJsonRequest: function() {
        var self = this;
        jsontemplates.searchRequest.DataSource = this.filter.dataSource.toInAJson();
        if (this.system instanceof global.sap.bc.ina.api.sina.impl.inav2.system.HANASystem) {
          if (this.attributes.length === 0) {
            throw {
              message: "Add at least one response attribute to your query"
            };
          }
          if (this.sqlSearch === true) {
            // TODO: remove workaround for SPS6. See CSS 0001971234 2013
            jsontemplates.searchRequest.Options = ["SqlSearch"];
          }
        }
        var selectedValues = [];
        for (var i = 0; i < this.attributes.length; ++i) {
          var attribute = this.attributes[i];
          if ($.type(attribute) === 'string') {
            selectedValues.push({
              Name: attribute,
              AttributeName: attribute
            });
          } else if ($.type(attribute) === 'object') {
            var selectedValue = {
              Name: attribute.name || attribute.attributeName,
              AttributeName: attribute.attributeName || attribute.name
            };
            if (attribute.highlighted === true) {
              selectedValue.Function = 'Highlighted';
            }
            if (attribute.snippet === true) {
              selectedValue.Function = 'Snippet';
            }
            if (attribute.startPosition !== undefined) {
              selectedValue.StartPosition = attribute.startPosition;
            }
            if (attribute.maxLength !== undefined) {
              selectedValue.MaxLength = attribute.maxLength;
            }
            selectedValues.push(selectedValue);
          }
        }
        jsontemplates.searchRequest.Search.SelectedValues = selectedValues;
        var searchterms = this.filter.getSearchTerms();
        if (!searchterms) {
          searchterms = '*';
        }
        jsontemplates.searchRequest.SearchTerms = searchterms;
        jsontemplates.searchRequest.Search.Top = this._top;
        jsontemplates.searchRequest.Search.Skip = this._skip;
        jsontemplates.searchRequest.Search.Filter = this.filter.getJson();
        jsontemplates.searchRequest.Search.OrderBy = this._assembleOrderBy();

        return jsontemplates.searchRequest;
      },

      /**
       * Sets how the result will be ordered.
       * @memberOf sap.bc.ina.api.sina.impl.inav2.sina_impl.SearchQuery
       * @instance
       * @param {Object|Array} orderBy If orderBy is an object, it must have the
       * properties 'orderBy' (string) and 'sortOrder' (string).
       * The orderBy property can either be the name of a database attribute that
       * the result will be sorted alphabetically for, or it can be the special
       * '$$score$$' string. The result will then be ordered according to the SAP HANA
       * Score function.
       * This function can also receive an array of these objects for multiple
       * order-by values, for example to order by $$score$$ and then alphabetically
       * for an attribute. The result will then be ordered after the first entry.
       * If two results have the same rank however, they will be ordered after the
       * second order-by value, and so on.
       * @default {orderBy:'$$score$$', sortOrder:'DESC'}
       * See {@link sap.bc.ina.api.sina.sinabase.Sina#createSearchQuery} for examples.
       */
      setOrderBy: function(orderBy) {
        this._resetResultSet();
        this.orderBy = orderBy || {
          orderBy: '$$score$$',
          sortOrder: 'DESC'
        };
      }

    });

    // =========================================================================
    // class result set
    // =========================================================================
    module.SearchResultSet.prototype = {

      /**
       * A result set suitable for a simple result list. An instance of this
       * class will be returned by {@link sap.bc.ina.api.sina.impl.inav2.sina_impl.SearchQuery#getResultSet}
       * @constructs sap.bc.ina.api.sina.impl.inav2.SearchResultSet
       * @private
       */
      init: function(properties) {
        properties = properties || {};
        this.elements = [];
        this.totalcount = 0;
      },

      toString: function(rs) {
          var elements = this.elements;
          var elements2 = [];
          var i;
          for (i = 0; i < elements.length; ++i) {
              var element = elements[i];
              var element2 = {};
              for (var attrName in element) {
                  if (attrName.slice(0, 2) === '$$' || attrName.slice(0, 1) === '_') {
                      continue;
                  }
                  var attrValue = element[attrName];
                  if (!attrValue.label || !attrValue.value) {
                      continue;
                  }
                  var attribute = {
                      label: attrValue.label,
                      value: attrValue.value
                  };
                  element2[attrName] = attribute;
              }
              elements2.push(element2);
          }
          return JSON.stringify(elements2);
      },

      setJsonData: function(data) {
        this.elements = [];

        function ResultElementRenderingTemplateSpecification() {
          this.type = "";
          this.platform = "";
          this.technology = "";
          this.width = "";
          this.height = "";
          this.variant = "";
          // this.description = "";
          this.uri = "";
          // this.request = null;
          // this.encodedJSON = "";
        }

        ResultElementRenderingTemplateSpecification.prototype = {

          _fromInaJson: function(inaJson) {
            this.type = inaJson.Type || "";
            this.platform = inaJson.Platform || "";
            this.technology = inaJson.Technology || "";
            this.width = inaJson.Width || "";
            this.height = inaJson.Height || "";
            this.variant = inaJson.Variant || "";
            // this.description = inaJson.Description || "";
            this.uri = inaJson.Uri || "";
          }

        };

        function ResultElementRelatedAction() {
          this.type = "";
          this.description = "";
          this.uri = "";
          this.request = null;
          this.encodedJSON = "";
        }

        ResultElementRelatedAction.prototype = {

          _fromInaJson: function(inaJson) {
            var self = this;
            self.description = inaJson.Description;
            self.uri = inaJson.Uri;

            switch (inaJson.Type) {
              case "RelatedRequest":
                self.type = 'Search';
                var queryProps = {};
                queryProps.dataSource = new filter.DataSource();
                queryProps.dataSource.fromInAJson(inaJson.Request.DataSource);
                queryProps.system = global.sap.bc.ina.api.sina.sinaSystem();
                queryProps.top = 1;
                self.request = new module.SearchQuery(queryProps);
                self.request.filter.setJson(inaJson.Request.Filter);
                // mark request without filter condition as invalid
                if (inaJson.Request.Filter.Selection === undefined) {
                  self.request.invalid = true;
                  self.request.invalidMessage = "Related request '" + self.description + "' is invalid because of missing filter conditions.";
                }
                break;
              case "GeneralUri":
                self.type = 'Link';
                self.url = self.uri;
                break;
              case "SAPNavigation":
                self.type = 'Navigation';
                self.url = self.uri;
                break;
            }
            self.encodedJSON = encodeURIComponent(self);
          }
        };

        function ResultElementAttributeMetaData(resultElementAttribute) {
          this.resultElementAttribute = resultElementAttribute;
          this.correspondingSearchAttributeName = "";
          this.description = "";
          this.isTitle = false;
          this.presentationUsage = [];
          this.displayOrder = null;
        }

        ResultElementAttributeMetaData.prototype = {

          _fromInaJson: function(inaAttributeMetaData) {
            var self = this;
            self.correspondingSearchAttributeName = inaAttributeMetaData.correspondingSearchAttributeName || "";
            self.description = inaAttributeMetaData.Description || "";
            self.presentationUsage = inaAttributeMetaData.presentationUsage || [];
            if (inaAttributeMetaData.IsTitle !== undefined) {
              self.isTitle = inaAttributeMetaData.IsTitle;
            }
            if (self.isTitle) {
              if (!self.resultElementAttribute.resultElement.title) {
                self.resultElementAttribute.resultElement.title = self.resultElementAttribute.resultElement.$$DataSourceMetaData$$.getLabel() + ":";
              }
              self.resultElementAttribute.resultElement._registerPostProcessor(function() {
                self.resultElementAttribute.resultElement.title = self.resultElementAttribute.resultElement.title + " " + self.resultElementAttribute.value;
              });
            }
          }

        };

        function ResultElementAttribute(resultElement) {
          this.resultElement = resultElement;
          this.$$MetaData$$ = new ResultElementAttributeMetaData(this);
          this.label = "";
          this.labelRaw = "";
          this.value = "";
          this.valueRaw = "";
        }

        ResultElementAttribute.prototype = {

          _fromInaJson: function(inaAttribute) {
            var self = this;
            self.labelRaw = this.$$MetaData$$.correspondingSearchAttributeName || inaAttribute.Name || "";
            self.label = this.$$MetaData$$.description || inaAttribute.Name || "";
            self.valueRaw = inaAttribute.Value || null;
            self.value = inaAttribute.ValueFormatted || null;

          },

          toString: function() {
            //stay compatible with result templates <= SAP HANA SPS 05
            return this.value;
          }
        };

        function ResultElement() {
          //these members are always provided:
          this.title = "";
          this.$$DataSourceMetaData$$ = {};
          this.$$RelatedActions$$ = {};
          this.$$RenderingTemplateSpecification$$ = {};
          this.$$WhyFound$$ = [];
          this.$$PostProcessors$$ = [];
          //the real result item attributes will be added to
          //this object dynamically
        }

        ResultElement.prototype = {

          _fromInaJson: function(namedValues) {
            var self = this;
            for (var k = 0; k < namedValues.length; ++k) {
              var namedValue = namedValues[k];
              switch (namedValue.Name) {
                case "$$DataSourceMetaData$$":
                  var dataSourceMetaData = namedValue.Value[0];
                  var dataSource = new filter.DataSource(dataSourceMetaData);
                  self.$$DataSourceMetaData$$ = dataSource;
                  break;
                case "$$AttributeMetadata$$":
                  for (var m = 0; m < namedValue.Value.length; ++m) {
                    var inaAttributeMetaData = namedValue.Value[m];
                    if (!self[inaAttributeMetaData.Name]) {
                      self[inaAttributeMetaData.Name] = new ResultElementAttribute(self);
                    }
                    self[inaAttributeMetaData.Name].$$MetaData$$._fromInaJson(inaAttributeMetaData);
                    self[inaAttributeMetaData.Name].$$MetaData$$.displayOrder = m;
                  }

                  break;
                case "$$ResultItemAttributes$$":
                  for (var l = 0; l < namedValue.Value.length; ++l) {
                    var inaAttribute = namedValue.Value[l];
                    if (!self[inaAttribute.Name]) {
                      self[inaAttribute.Name] = new ResultElementAttribute(self);
                    }
                    self[inaAttribute.Name]._fromInaJson(inaAttribute);
                  }
                  break;
                case "$$RelatedActions$$":
                  var actions = {};
                  for (var n = 0; n < namedValue.Value.length; ++n) {
                    var action = namedValue.Value[n];
                    var sinaAction = new ResultElementRelatedAction();
                    sinaAction._fromInaJson(action);
                    actions[action.ID] = sinaAction;
                  }
                  self.$$RelatedActions$$ = actions;
                  break;
                case "$$RenderingTemplateSpecification$$":
                  for (var o = 0; o < namedValue.Value.length; ++o) {
                    var template = new ResultElementRenderingTemplateSpecification();
                    template._fromInaJson(namedValue.Value[o]);
                    // template = propertiesToLowerCase(template);
                    if (template.type === "ItemDetails") {
                      self._detailTemplate = template; //save for later, so no 2nd request is needed
                    } else {
                      self.$$RenderingTemplateSpecification$$ = template;
                    }
                  }
                  break;
                case "$$WhyFound$$":
                  for (var z = 0; z < namedValue.Value.length; ++z) {
                    var whyfoundElem = {};
                    whyfoundElem.label = namedValue.Value[z].Description;
                    whyfoundElem.labelRaw = namedValue.Value[z].Name;
                    whyfoundElem.value = namedValue.Value[z].Value;
                    whyfoundElem.valueHighlighted = whyfoundElem.value;
                    whyfoundElem.valueRaw = namedValue.Value[z].Value;
                    self.$$WhyFound$$.push(whyfoundElem);
                  }
                  break;
                default:
                  // we assume thats a (HANA InA) result element:
                  self[namedValue.Name] = new ResultElementAttribute();
                  self[namedValue.Name].label = namedValue.Name || "";
                  self[namedValue.Name].valueRaw = namedValue.Value || namedValue.ValueFormatted || null;
                  self[namedValue.Name].value = namedValue.ValueFormatted || namedValue.Value || null;
              }
            }
            for (var i = 0; i < this.$$PostProcessors$$.length; i++) {
              this.$$PostProcessors$$[i]();
            }
          },

          _registerPostProcessor: function(fn) {
            this.$$PostProcessors$$.push(fn);
          }


        };

        function _prepareDetails(element) {
          // TODO: remove service workaround: if there is no detail query -> try to create
          if (element._detailTemplate && !element.$$RelatedActions$$.$$DETAILS$$) {
            element.$$RelatedActions$$.$$DETAILS$$ = {
              request: new module.SearchQuery(),
              description: '',
              encodedJSON: '',
              type: 'Search',
              uri: ''
            };
          }
          // End of workaround
          // prefill result set of detail query
          if (element._detailTemplate && element.$$RelatedActions$$.$$DETAILS$$) {
            var detailResultSet = new module.SearchResultSet();
            detailResultSet.elements[0] = $.extend(true, {}, element);
            detailResultSet.elements[0].$$RenderingTemplateSpecification$$ = element._detailTemplate;
            detailResultSet.totalcount = 1;
            delete detailResultSet.elements[0].$$RelatedActions$$.$$DETAILS$$;
            element.$$RelatedActions$$.$$DETAILS$$.request.resultSet = detailResultSet;
            return detailResultSet.elements[0];
          }
          // return function(onSuccess,onError){
          //     if(this._detailResultSet){
          //         if(onSuccess){
          //             onSuccess(this._detailResultSet);
          //         }
          //     }
          //     else{
          //         this.$$RelatedActions$$.$$DETAILS$$.request.getResultSet(onSuccess,onError);
          //     }
          // };
          return {};
        }

        var itemLists = {};
        if (!data.ItemLists) {
          return {};
        }
        for (var i = 0; i < data.ItemLists.length; i++) {
          itemLists[data.ItemLists[i].Name] = data.ItemLists[i];
          if (data.ItemLists[i].Name.toLowerCase() === "searchresult") {
            this.totalcount = data.ItemLists[i].TotalCount.Value;
          }
        }

        var axis0;
        if (data && data.Grids && data.Grids[0] && data.Grids[0].Axes && data.Grids[0].Axes[0]) {
          axis0 = data.Grids[0].Axes[0];
        } else {
          return {};
        }
        // only axes 0 is relevant for abap search results
        for (var j = 0; j < axis0.Tuples.length; j++) {
          var tuple = axis0.Tuples[j];
          if (tuple === undefined) {
            continue;
          }
          var element = new ResultElement();
          for (var c = tuple.length - 1; c >= 0; c--) {
            // for (var c = 0; c < tuple.length; c++) {
            var dimension = axis0.Dimensions[c];
            var tupleValueForDimension = tuple[c];
            var itemlist = itemLists[dimension.ItemListName];
            var namedValues = itemlist.Items[tupleValueForDimension].NamedValues;
            element._fromInaJson(namedValues);
          }

          if (axis0 && axis0.Dimensions[1]) {
            var dimensionMetaData = axis0.Dimensions[1];
            var itemlistMetaData = itemLists[dimensionMetaData.ItemListName];
            var pointer2MyMetaData = tuple[1];
            if (itemlistMetaData && itemlistMetaData.Items && itemlistMetaData.Items[pointer2MyMetaData]) {
              var namedValuesAttributeMetadata = itemlistMetaData.Items[pointer2MyMetaData].NamedValues[2];
              if (namedValuesAttributeMetadata) {
                element = this._postProcess4WhyFound(element, namedValuesAttributeMetadata.Value);
              }
            }
          }

          var detail = _prepareDetails(element);
          this._postProcessRelatedAction(element);
          if (detail) {
            this._postProcessRelatedAction(detail);
          }
          this.elements.push(element);
        }
        return {};
      },

      /**
             * Returns the elements of the result set.
             * @memberOf sap.bc.ina.api.sina.impl.inav2.sina_impl.SearchResultSet
             * @instance
             * @since SAP HANA SPS 06
             * @return {Array} A list of result set elements.
             * @example
             * var query = sap.bc.ina.api.sina.createSearchQuery({
                dataSource          : { schemaName  : "SYSTEM",
                                        objectName  : "J_EPM_PRODUCT" },
                attributes          : [ "PRODUCT_ID",
                                        "TEXT",
                                        "CATEGORY",
                                        "PRICE",
                                        "CURRENCY_CODE"],
                searchTerms         : "basic",
                top                 : 5
               });
             * var resultSet = query.getResultSetSync();
             * var elements = resultSet.getElements();
             * // contents of elements (shortened):
             * [{ "PRODUCT_ID":{"label":"PRODUCT_ID","valueRaw":"HT-1000","value":"HT-1000"},
             *     "TEXT":{"label":"TEXT","valueRaw":"Notebook Basic 15 with 1,7GHz - 15","value":"Notebook Basic 15 with 1,7GHz - 15"},
             *     "CATEGORY":{"label":"CATEGORY","valueRaw":"Notebooks","value":"Notebooks"},
             *     "PRICE":{"label":"PRICE","valueRaw":"956.00","value":"956.00"},
             *     "CURRENCY_CODE":{"label":"CURRENCY_CODE","valueRaw":"EUR","value":"EUR"}},
             *     // second result item:
             *     {"PRODUCT_ID":{"label":"PRODUCT_ID","valueRaw":"HT-1001","value":"HT-1001"},
             *     "TEXT":{"label":"TEXT","valueRaw":"Notebook Basic 17 with 1,7GHz - 17","value":"Notebook Basic 17 with 1,7GHz - 17"},
             *     "CATEGORY":{"label":"CATEGORY","valueRaw":"Notebooks","value":"Notebooks"},
             *     "PRICE":{"label":"PRICE","valueRaw":"1249.00","value":"1249.00"},
             *     "CURRENCY_CODE":{"label":"CURRENCY_CODE","valueRaw":"EUR","value":"EUR"}},
             *     // third result item:
             *     {"PRODUCT_ID":{"label":"PRODUCT_ID","valueRaw":"HT-1002","value":"HT-1002"},
             *     "TEXT":{"label":"TEXT","valueRaw":"Notebook Basic 18 with 1,7GHz - 18","value":"Notebook Basic 18 with 1,7GHz - 18"},
             *     "CATEGORY":{"label":"CATEGORY","valueRaw":"Notebooks","value":"Notebooks"},
             *     "PRICE":{"label":"PRICE","valueRaw":"1570.00","value":"1570.00"},
             *     "CURRENCY_CODE":{"label":"CURRENCY_CODE","valueRaw":"USD","value":"USD"}},
             *     // fourth result item:
             *     {"PRODUCT_ID":{"label":"PRODUCT_ID","valueRaw":"HT-1003","value":"HT-1003"},
             *     "TEXT":{"label":"TEXT","valueRaw":"Notebook Basic 19 with 1,7GHz - 19","value":"Notebook Basic 19 with 1,7GHz - 19"},
             *     "CATEGORY":{"label":"CATEGORY","valueRaw":"Notebooks","value":"Notebooks"},
             *     "PRICE":{"label":"PRICE","valueRaw":"1650.00","value":"1650.00"},
             *     "CURRENCY_CODE":{"label":"CURRENCY_CODE","valueRaw":"EUR","value":"EUR"}},
             *     // fifth result item:
             *     {"PRODUCT_ID":{"label":"PRODUCT_ID","valueRaw":"HT-8000","value":"HT-8000"},
             *     "TEXT":{"label":"TEXT","valueRaw":"1,5 Ghz, single core, 40 GB HDD, Windows Vista Home Basic, 512 MB RAM","value":"1,5 Ghz, single core, 40 GB HDD, Windows Vista Home Basic, 512 MB RAM"},
             *     "CATEGORY":{"label":"CATEGORY","valueRaw":"Notebooks","value":"Notebooks"},
             *     "PRICE":{"label":"PRICE","valueRaw":"799.00","value":"799.00"},
             *     "CURRENCY_CODE":{"label":"CURRENCY_CODE","valueRaw":"EUR","value":"EUR"}}
             *  ]
             */
      getElements: function() {
        return this.elements;
      },

      _postProcessRelatedAction: function(element) {
        if (!element.$$RelatedActions$$) {
          return;
        }
        for (var relatedActionID in element.$$RelatedActions$$) {
          var relatedAction = element.$$RelatedActions$$[relatedActionID];
          //Postprocessing
          if (relatedAction.type === "Search") {
            global.sap.bc.ina.api.sina._postprocess(relatedAction, element.title);
          }
        }
      },

      _postProcess4WhyFound: function(element, metaAttributes) {
        if (element.$$WhyFound$$ && element.$$WhyFound$$.length > 0) {
          var i = element.$$WhyFound$$.length;
          var value;
          var hasResponseAttribute;
          while (i--) {
            hasResponseAttribute = false;
            if (element[element.$$WhyFound$$[i].labelRaw] !== undefined && metaAttributes !== undefined) {
              ////                        value = element.$$WhyFound$$[i].value.replace(/<b>/g, '<div class="InA-highlighter" data-sap-widget="highlighter">').replace(/<\/b>/g, '</div">');
              //                        value = element.$$WhyFound$$[i].value;
              //                        element[element.$$WhyFound$$[i].labelRaw].value    = value;
              //                        element[element.$$WhyFound$$[i].labelRaw].valueRaw = value;
              //                        element.$$WhyFound$$.splice(i,1);
              var j = metaAttributes.length;
              while (j--) {
                // The WhyFound attributes are requst attributes. Try to get its corresponding response attribute
                if (metaAttributes[j].Name === element.$$WhyFound$$[i].labelRaw && metaAttributes[j].correspondingSearchAttributeName) {
                  element.$$WhyFound$$[i].labelRaw = metaAttributes[j].correspondingSearchAttributeName;
                  hasResponseAttribute = true;
                }
              }
            }
            //                    if (!hasResponseAttribute){
            //                        element.$$WhyFound$$[i].label = element.$$WhyFound$$[i].label + " (modeling error: add missing corresponding response attribute!)";
            //                    }
          }
        }
        return element;
      }
    };
  };

  if (isXS) {
    executeSinaImpl($.sap.bc.ina.api.sina.sinaxsjq.jq);
  } else if (typeof define === "function" && define.amd && !global.sap.ushell) { // only concatenated sina is in ushell repo!
    define(["jquery", "./filter", "../../sinabase", "./system", "./proxy"], function($) {
      executeSinaImpl($);
    });
  } else {
    executeSinaImpl();
  }

}(typeof window === 'undefined' ? $ : window, typeof window === 'undefined'));