| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| // TODO |
| // ??? refactor? check the outer usage of data provider. |
| // merge with defaultDimValueGetter? |
| import { __DEV__ } from '../../config'; |
| import { isTypedArray, extend, assert, each, isObject } from 'zrender/src/core/util'; |
| import { getDataItemValue, isDataItemOption } from '../../util/model'; |
| import { parseDate } from '../../util/number'; |
| import Source from '../Source'; |
| import { SOURCE_FORMAT_TYPED_ARRAY, SOURCE_FORMAT_ARRAY_ROWS, SOURCE_FORMAT_ORIGINAL, SOURCE_FORMAT_OBJECT_ROWS } from './sourceType'; |
| /** |
| * If normal array used, mutable chunk size is supported. |
| * If typed array used, chunk size must be fixed. |
| */ |
| |
| export function DefaultDataProvider(source, dimSize) { |
| if (!Source.isInstance(source)) { |
| source = Source.seriesDataToSource(source); |
| } |
| |
| this._source = source; |
| var data = this._data = source.data; |
| var sourceFormat = source.sourceFormat; // Typed array. TODO IE10+? |
| |
| if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) { |
| this._offset = 0; |
| this._dimSize = dimSize; |
| this._data = data; |
| } |
| |
| var methods = providerMethods[sourceFormat === SOURCE_FORMAT_ARRAY_ROWS ? sourceFormat + '_' + source.seriesLayoutBy : sourceFormat]; |
| extend(this, methods); |
| } |
| var providerProto = DefaultDataProvider.prototype; // If data is pure without style configuration |
| |
| providerProto.pure = false; // If data is persistent and will not be released after use. |
| |
| providerProto.persistent = true; // ???! FIXME legacy data provider do not has method getSource |
| |
| providerProto.getSource = function () { |
| return this._source; |
| }; |
| |
| var providerMethods = { |
| 'arrayRows_column': { |
| pure: true, |
| count: function () { |
| return Math.max(0, this._data.length - this._source.startIndex); |
| }, |
| getItem: function (idx) { |
| return this._data[idx + this._source.startIndex]; |
| }, |
| appendData: appendDataSimply |
| }, |
| 'arrayRows_row': { |
| pure: true, |
| count: function () { |
| var row = this._data[0]; |
| return row ? Math.max(0, row.length - this._source.startIndex) : 0; |
| }, |
| getItem: function (idx) { |
| idx += this._source.startIndex; |
| var item = []; |
| var data = this._data; |
| |
| for (var i = 0; i < data.length; i++) { |
| var row = data[i]; |
| item.push(row ? row[idx] : null); |
| } |
| |
| return item; |
| }, |
| appendData: function () { |
| throw new Error('Do not support appendData when set seriesLayoutBy: "row".'); |
| } |
| }, |
| 'objectRows': { |
| pure: true, |
| count: countSimply, |
| getItem: getItemSimply, |
| appendData: appendDataSimply |
| }, |
| 'keyedColumns': { |
| pure: true, |
| count: function () { |
| var dimName = this._source.dimensionsDefine[0].name; |
| var col = this._data[dimName]; |
| return col ? col.length : 0; |
| }, |
| getItem: function (idx) { |
| var item = []; |
| var dims = this._source.dimensionsDefine; |
| |
| for (var i = 0; i < dims.length; i++) { |
| var col = this._data[dims[i].name]; |
| item.push(col ? col[idx] : null); |
| } |
| |
| return item; |
| }, |
| appendData: function (newData) { |
| var data = this._data; |
| each(newData, function (newCol, key) { |
| var oldCol = data[key] || (data[key] = []); |
| |
| for (var i = 0; i < (newCol || []).length; i++) { |
| oldCol.push(newCol[i]); |
| } |
| }); |
| } |
| }, |
| 'original': { |
| count: countSimply, |
| getItem: getItemSimply, |
| appendData: appendDataSimply |
| }, |
| 'typedArray': { |
| persistent: false, |
| pure: true, |
| count: function () { |
| return this._data ? this._data.length / this._dimSize : 0; |
| }, |
| getItem: function (idx, out) { |
| idx = idx - this._offset; |
| out = out || []; |
| var offset = this._dimSize * idx; |
| |
| for (var i = 0; i < this._dimSize; i++) { |
| out[i] = this._data[offset + i]; |
| } |
| |
| return out; |
| }, |
| appendData: function (newData) { |
| this._data = newData; |
| }, |
| // Clean self if data is already used. |
| clean: function () { |
| // PENDING |
| this._offset += this.count(); |
| this._data = null; |
| } |
| } |
| }; |
| |
| function countSimply() { |
| return this._data.length; |
| } |
| |
| function getItemSimply(idx) { |
| return this._data[idx]; |
| } |
| |
| function appendDataSimply(newData) { |
| for (var i = 0; i < newData.length; i++) { |
| this._data.push(newData[i]); |
| } |
| } |
| |
| var rawValueGetters = { |
| arrayRows: getRawValueSimply, |
| objectRows: function (dataItem, dataIndex, dimIndex, dimName) { |
| return dimIndex != null ? dataItem[dimName] : dataItem; |
| }, |
| keyedColumns: getRawValueSimply, |
| original: function (dataItem, dataIndex, dimIndex, dimName) { |
| // FIXME |
| // In some case (markpoint in geo (geo-map.html)), dataItem |
| // is {coord: [...]} |
| var value = getDataItemValue(dataItem); |
| return dimIndex == null || !(value instanceof Array) ? value : value[dimIndex]; |
| }, |
| typedArray: getRawValueSimply |
| }; |
| |
| function getRawValueSimply(dataItem, dataIndex, dimIndex, dimName) { |
| return dimIndex != null ? dataItem[dimIndex] : dataItem; |
| } |
| |
| export var defaultDimValueGetters = { |
| arrayRows: getDimValueSimply, |
| objectRows: function (dataItem, dimName, dataIndex, dimIndex) { |
| return converDataValue(dataItem[dimName], this._dimensionInfos[dimName]); |
| }, |
| keyedColumns: getDimValueSimply, |
| original: function (dataItem, dimName, dataIndex, dimIndex) { |
| // Performance sensitive, do not use modelUtil.getDataItemValue. |
| // If dataItem is an plain object with no value field, the var `value` |
| // will be assigned with the object, but it will be tread correctly |
| // in the `convertDataValue`. |
| var value = dataItem && (dataItem.value == null ? dataItem : dataItem.value); // If any dataItem is like { value: 10 } |
| |
| if (!this._rawData.pure && isDataItemOption(dataItem)) { |
| this.hasItemOption = true; |
| } |
| |
| return converDataValue(value instanceof Array ? value[dimIndex] // If value is a single number or something else not array. |
| : value, this._dimensionInfos[dimName]); |
| }, |
| typedArray: function (dataItem, dimName, dataIndex, dimIndex) { |
| return dataItem[dimIndex]; |
| } |
| }; |
| |
| function getDimValueSimply(dataItem, dimName, dataIndex, dimIndex) { |
| return converDataValue(dataItem[dimIndex], this._dimensionInfos[dimName]); |
| } |
| /** |
| * This helper method convert value in data. |
| * @param {string|number|Date} value |
| * @param {Object|string} [dimInfo] If string (like 'x'), dimType defaults 'number'. |
| * If "dimInfo.ordinalParseAndSave", ordinal value can be parsed. |
| */ |
| |
| |
| function converDataValue(value, dimInfo) { |
| // Performance sensitive. |
| var dimType = dimInfo && dimInfo.type; |
| |
| if (dimType === 'ordinal') { |
| // If given value is a category string |
| var ordinalMeta = dimInfo && dimInfo.ordinalMeta; |
| return ordinalMeta ? ordinalMeta.parseAndCollect(value) : value; |
| } |
| |
| if (dimType === 'time' // spead up when using timestamp |
| && typeof value !== 'number' && value != null && value !== '-') { |
| value = +parseDate(value); |
| } // dimType defaults 'number'. |
| // If dimType is not ordinal and value is null or undefined or NaN or '-', |
| // parse to NaN. |
| |
| |
| return value == null || value === '' ? NaN // If string (like '-'), using '+' parse to NaN |
| // If object, also parse to NaN |
| : +value; |
| } // ??? FIXME can these logic be more neat: getRawValue, getRawDataItem, |
| // Consider persistent. |
| // Caution: why use raw value to display on label or tooltip? |
| // A reason is to avoid format. For example time value we do not know |
| // how to format is expected. More over, if stack is used, calculated |
| // value may be 0.91000000001, which have brings trouble to display. |
| // TODO: consider how to treat null/undefined/NaN when display? |
| |
| /** |
| * @param {module:echarts/data/List} data |
| * @param {number} dataIndex |
| * @param {string|number} [dim] dimName or dimIndex |
| * @return {Array.<number>|string|number} can be null/undefined. |
| */ |
| |
| |
| export function retrieveRawValue(data, dataIndex, dim) { |
| if (!data) { |
| return; |
| } // Consider data may be not persistent. |
| |
| |
| var dataItem = data.getRawDataItem(dataIndex); |
| |
| if (dataItem == null) { |
| return; |
| } |
| |
| var sourceFormat = data.getProvider().getSource().sourceFormat; |
| var dimName; |
| var dimIndex; |
| var dimInfo = data.getDimensionInfo(dim); |
| |
| if (dimInfo) { |
| dimName = dimInfo.name; |
| dimIndex = dimInfo.index; |
| } |
| |
| return rawValueGetters[sourceFormat](dataItem, dataIndex, dimIndex, dimName); |
| } |
| /** |
| * Compatible with some cases (in pie, map) like: |
| * data: [{name: 'xx', value: 5, selected: true}, ...] |
| * where only sourceFormat is 'original' and 'objectRows' supported. |
| * |
| * ??? TODO |
| * Supported detail options in data item when using 'arrayRows'. |
| * |
| * @param {module:echarts/data/List} data |
| * @param {number} dataIndex |
| * @param {string} attr like 'selected' |
| */ |
| |
| export function retrieveRawAttr(data, dataIndex, attr) { |
| if (!data) { |
| return; |
| } |
| |
| var sourceFormat = data.getProvider().getSource().sourceFormat; |
| |
| if (sourceFormat !== SOURCE_FORMAT_ORIGINAL && sourceFormat !== SOURCE_FORMAT_OBJECT_ROWS) { |
| return; |
| } |
| |
| var dataItem = data.getRawDataItem(dataIndex); |
| |
| if (sourceFormat === SOURCE_FORMAT_ORIGINAL && !isObject(dataItem)) { |
| dataItem = null; |
| } |
| |
| if (dataItem) { |
| return dataItem[attr]; |
| } |
| } |