417 lines
16 KiB
JavaScript
417 lines
16 KiB
JavaScript
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
|
|
/**
|
|
* AUTO-GENERATED FILE. DO NOT MODIFY.
|
|
*/
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
import { SERIES_LAYOUT_BY_COLUMN, SOURCE_FORMAT_OBJECT_ROWS, SOURCE_FORMAT_ARRAY_ROWS } from '../../util/types.js';
|
|
import { normalizeToArray } from '../../util/model.js';
|
|
import { createHashMap, bind, each, hasOwn, map, clone, isObject, extend, isNumber } from 'zrender/lib/core/util.js';
|
|
import { getRawSourceItemGetter, getRawSourceDataCounter, getRawSourceValueGetter } from './dataProvider.js';
|
|
import { parseDataValue } from './dataValueHelper.js';
|
|
import { log, makePrintable, throwError } from '../../util/log.js';
|
|
import { createSource, detectSourceFormat } from '../Source.js';
|
|
/**
|
|
* TODO: disable writable.
|
|
* This structure will be exposed to users.
|
|
*/
|
|
var ExternalSource = /** @class */function () {
|
|
function ExternalSource() {}
|
|
ExternalSource.prototype.getRawData = function () {
|
|
// Only built-in transform available.
|
|
throw new Error('not supported');
|
|
};
|
|
ExternalSource.prototype.getRawDataItem = function (dataIndex) {
|
|
// Only built-in transform available.
|
|
throw new Error('not supported');
|
|
};
|
|
ExternalSource.prototype.cloneRawData = function () {
|
|
return;
|
|
};
|
|
/**
|
|
* @return If dimension not found, return null/undefined.
|
|
*/
|
|
ExternalSource.prototype.getDimensionInfo = function (dim) {
|
|
return;
|
|
};
|
|
/**
|
|
* dimensions defined if and only if either:
|
|
* (a) dataset.dimensions are declared.
|
|
* (b) dataset data include dimensions definitions in data (detected or via specified `sourceHeader`).
|
|
* If dimensions are defined, `dimensionInfoAll` is corresponding to
|
|
* the defined dimensions.
|
|
* Otherwise, `dimensionInfoAll` is determined by data columns.
|
|
* @return Always return an array (even empty array).
|
|
*/
|
|
ExternalSource.prototype.cloneAllDimensionInfo = function () {
|
|
return;
|
|
};
|
|
ExternalSource.prototype.count = function () {
|
|
return;
|
|
};
|
|
/**
|
|
* Only support by dimension index.
|
|
* No need to support by dimension name in transform function,
|
|
* because transform function is not case-specific, no need to use name literally.
|
|
*/
|
|
ExternalSource.prototype.retrieveValue = function (dataIndex, dimIndex) {
|
|
return;
|
|
};
|
|
ExternalSource.prototype.retrieveValueFromItem = function (dataItem, dimIndex) {
|
|
return;
|
|
};
|
|
ExternalSource.prototype.convertValue = function (rawVal, dimInfo) {
|
|
return parseDataValue(rawVal, dimInfo);
|
|
};
|
|
return ExternalSource;
|
|
}();
|
|
export { ExternalSource };
|
|
function createExternalSource(internalSource, externalTransform) {
|
|
var extSource = new ExternalSource();
|
|
var data = internalSource.data;
|
|
var sourceFormat = extSource.sourceFormat = internalSource.sourceFormat;
|
|
var sourceHeaderCount = internalSource.startIndex;
|
|
var errMsg = '';
|
|
if (internalSource.seriesLayoutBy !== SERIES_LAYOUT_BY_COLUMN) {
|
|
// For the logic simplicity in transformer, only 'culumn' is
|
|
// supported in data transform. Otherwise, the `dimensionsDefine`
|
|
// might be detected by 'row', which probably confuses users.
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
errMsg = '`seriesLayoutBy` of upstream dataset can only be "column" in data transform.';
|
|
}
|
|
throwError(errMsg);
|
|
}
|
|
// [MEMO]
|
|
// Create a new dimensions structure for exposing.
|
|
// Do not expose all dimension info to users directly.
|
|
// Because the dimension is probably auto detected from data and not might reliable.
|
|
// Should not lead the transformers to think that is reliable and return it.
|
|
// See [DIMENSION_INHERIT_RULE] in `sourceManager.ts`.
|
|
var dimensions = [];
|
|
var dimsByName = {};
|
|
var dimsDef = internalSource.dimensionsDefine;
|
|
if (dimsDef) {
|
|
each(dimsDef, function (dimDef, idx) {
|
|
var name = dimDef.name;
|
|
var dimDefExt = {
|
|
index: idx,
|
|
name: name,
|
|
displayName: dimDef.displayName
|
|
};
|
|
dimensions.push(dimDefExt);
|
|
// Users probably do not specify dimension name. For simplicity, data transform
|
|
// does not generate dimension name.
|
|
if (name != null) {
|
|
// Dimension name should not be duplicated.
|
|
// For simplicity, data transform forbids name duplication, do not generate
|
|
// new name like module `completeDimensions.ts` did, but just tell users.
|
|
var errMsg_1 = '';
|
|
if (hasOwn(dimsByName, name)) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
errMsg_1 = 'dimension name "' + name + '" duplicated.';
|
|
}
|
|
throwError(errMsg_1);
|
|
}
|
|
dimsByName[name] = dimDefExt;
|
|
}
|
|
});
|
|
}
|
|
// If dimension definitions are not defined and can not be detected.
|
|
// e.g., pure data `[[11, 22], ...]`.
|
|
else {
|
|
for (var i = 0; i < internalSource.dimensionsDetectedCount || 0; i++) {
|
|
// Do not generete name or anything others. The consequence process in
|
|
// `transform` or `series` probably have there own name generation strategry.
|
|
dimensions.push({
|
|
index: i
|
|
});
|
|
}
|
|
}
|
|
// Implement public methods:
|
|
var rawItemGetter = getRawSourceItemGetter(sourceFormat, SERIES_LAYOUT_BY_COLUMN);
|
|
if (externalTransform.__isBuiltIn) {
|
|
extSource.getRawDataItem = function (dataIndex) {
|
|
return rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex);
|
|
};
|
|
extSource.getRawData = bind(getRawData, null, internalSource);
|
|
}
|
|
extSource.cloneRawData = bind(cloneRawData, null, internalSource);
|
|
var rawCounter = getRawSourceDataCounter(sourceFormat, SERIES_LAYOUT_BY_COLUMN);
|
|
extSource.count = bind(rawCounter, null, data, sourceHeaderCount, dimensions);
|
|
var rawValueGetter = getRawSourceValueGetter(sourceFormat);
|
|
extSource.retrieveValue = function (dataIndex, dimIndex) {
|
|
var rawItem = rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex);
|
|
return retrieveValueFromItem(rawItem, dimIndex);
|
|
};
|
|
var retrieveValueFromItem = extSource.retrieveValueFromItem = function (dataItem, dimIndex) {
|
|
if (dataItem == null) {
|
|
return;
|
|
}
|
|
var dimDef = dimensions[dimIndex];
|
|
// When `dimIndex` is `null`, `rawValueGetter` return the whole item.
|
|
if (dimDef) {
|
|
return rawValueGetter(dataItem, dimIndex, dimDef.name);
|
|
}
|
|
};
|
|
extSource.getDimensionInfo = bind(getDimensionInfo, null, dimensions, dimsByName);
|
|
extSource.cloneAllDimensionInfo = bind(cloneAllDimensionInfo, null, dimensions);
|
|
return extSource;
|
|
}
|
|
function getRawData(upstream) {
|
|
var sourceFormat = upstream.sourceFormat;
|
|
if (!isSupportedSourceFormat(sourceFormat)) {
|
|
var errMsg = '';
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
errMsg = '`getRawData` is not supported in source format ' + sourceFormat;
|
|
}
|
|
throwError(errMsg);
|
|
}
|
|
return upstream.data;
|
|
}
|
|
function cloneRawData(upstream) {
|
|
var sourceFormat = upstream.sourceFormat;
|
|
var data = upstream.data;
|
|
if (!isSupportedSourceFormat(sourceFormat)) {
|
|
var errMsg = '';
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
errMsg = '`cloneRawData` is not supported in source format ' + sourceFormat;
|
|
}
|
|
throwError(errMsg);
|
|
}
|
|
if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {
|
|
var result = [];
|
|
for (var i = 0, len = data.length; i < len; i++) {
|
|
// Not strictly clone for performance
|
|
result.push(data[i].slice());
|
|
}
|
|
return result;
|
|
} else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {
|
|
var result = [];
|
|
for (var i = 0, len = data.length; i < len; i++) {
|
|
// Not strictly clone for performance
|
|
result.push(extend({}, data[i]));
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
function getDimensionInfo(dimensions, dimsByName, dim) {
|
|
if (dim == null) {
|
|
return;
|
|
}
|
|
// Keep the same logic as `List::getDimension` did.
|
|
if (isNumber(dim)
|
|
// If being a number-like string but not being defined a dimension name.
|
|
|| !isNaN(dim) && !hasOwn(dimsByName, dim)) {
|
|
return dimensions[dim];
|
|
} else if (hasOwn(dimsByName, dim)) {
|
|
return dimsByName[dim];
|
|
}
|
|
}
|
|
function cloneAllDimensionInfo(dimensions) {
|
|
return clone(dimensions);
|
|
}
|
|
var externalTransformMap = createHashMap();
|
|
export function registerExternalTransform(externalTransform) {
|
|
externalTransform = clone(externalTransform);
|
|
var type = externalTransform.type;
|
|
var errMsg = '';
|
|
if (!type) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
errMsg = 'Must have a `type` when `registerTransform`.';
|
|
}
|
|
throwError(errMsg);
|
|
}
|
|
var typeParsed = type.split(':');
|
|
if (typeParsed.length !== 2) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
errMsg = 'Name must include namespace like "ns:regression".';
|
|
}
|
|
throwError(errMsg);
|
|
}
|
|
// Namespace 'echarts:xxx' is official namespace, where the transforms should
|
|
// be called directly via 'xxx' rather than 'echarts:xxx'.
|
|
var isBuiltIn = false;
|
|
if (typeParsed[0] === 'echarts') {
|
|
type = typeParsed[1];
|
|
isBuiltIn = true;
|
|
}
|
|
externalTransform.__isBuiltIn = isBuiltIn;
|
|
externalTransformMap.set(type, externalTransform);
|
|
}
|
|
export function applyDataTransform(rawTransOption, sourceList, infoForPrint) {
|
|
var pipedTransOption = normalizeToArray(rawTransOption);
|
|
var pipeLen = pipedTransOption.length;
|
|
var errMsg = '';
|
|
if (!pipeLen) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
errMsg = 'If `transform` declared, it should at least contain one transform.';
|
|
}
|
|
throwError(errMsg);
|
|
}
|
|
for (var i = 0, len = pipeLen; i < len; i++) {
|
|
var transOption = pipedTransOption[i];
|
|
sourceList = applySingleDataTransform(transOption, sourceList, infoForPrint, pipeLen === 1 ? null : i);
|
|
// piped transform only support single input, except the fist one.
|
|
// piped transform only support single output, except the last one.
|
|
if (i !== len - 1) {
|
|
sourceList.length = Math.max(sourceList.length, 1);
|
|
}
|
|
}
|
|
return sourceList;
|
|
}
|
|
function applySingleDataTransform(transOption, upSourceList, infoForPrint,
|
|
// If `pipeIndex` is null/undefined, no piped transform.
|
|
pipeIndex) {
|
|
var errMsg = '';
|
|
if (!upSourceList.length) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
errMsg = 'Must have at least one upstream dataset.';
|
|
}
|
|
throwError(errMsg);
|
|
}
|
|
if (!isObject(transOption)) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
errMsg = 'transform declaration must be an object rather than ' + typeof transOption + '.';
|
|
}
|
|
throwError(errMsg);
|
|
}
|
|
var transType = transOption.type;
|
|
var externalTransform = externalTransformMap.get(transType);
|
|
if (!externalTransform) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
errMsg = 'Can not find transform on type "' + transType + '".';
|
|
}
|
|
throwError(errMsg);
|
|
}
|
|
// Prepare source
|
|
var extUpSourceList = map(upSourceList, function (upSource) {
|
|
return createExternalSource(upSource, externalTransform);
|
|
});
|
|
var resultList = normalizeToArray(externalTransform.transform({
|
|
upstream: extUpSourceList[0],
|
|
upstreamList: extUpSourceList,
|
|
config: clone(transOption.config)
|
|
}));
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
if (transOption.print) {
|
|
var printStrArr = map(resultList, function (extSource) {
|
|
var pipeIndexStr = pipeIndex != null ? ' === pipe index: ' + pipeIndex : '';
|
|
return ['=== dataset index: ' + infoForPrint.datasetIndex + pipeIndexStr + ' ===', '- transform result data:', makePrintable(extSource.data), '- transform result dimensions:', makePrintable(extSource.dimensions)].join('\n');
|
|
}).join('\n');
|
|
log(printStrArr);
|
|
}
|
|
}
|
|
return map(resultList, function (result, resultIndex) {
|
|
var errMsg = '';
|
|
if (!isObject(result)) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
errMsg = 'A transform should not return some empty results.';
|
|
}
|
|
throwError(errMsg);
|
|
}
|
|
if (!result.data) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
errMsg = 'Transform result data should be not be null or undefined';
|
|
}
|
|
throwError(errMsg);
|
|
}
|
|
var sourceFormat = detectSourceFormat(result.data);
|
|
if (!isSupportedSourceFormat(sourceFormat)) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
errMsg = 'Transform result data should be array rows or object rows.';
|
|
}
|
|
throwError(errMsg);
|
|
}
|
|
var resultMetaRawOption;
|
|
var firstUpSource = upSourceList[0];
|
|
/**
|
|
* Intuitively, the end users known the content of the original `dataset.source`,
|
|
* calucating the transform result in mind.
|
|
* Suppose the original `dataset.source` is:
|
|
* ```js
|
|
* [
|
|
* ['product', '2012', '2013', '2014', '2015'],
|
|
* ['AAA', 41.1, 30.4, 65.1, 53.3],
|
|
* ['BBB', 86.5, 92.1, 85.7, 83.1],
|
|
* ['CCC', 24.1, 67.2, 79.5, 86.4]
|
|
* ]
|
|
* ```
|
|
* The dimension info have to be detected from the source data.
|
|
* Some of the transformers (like filter, sort) will follow the dimension info
|
|
* of upstream, while others use new dimensions (like aggregate).
|
|
* Transformer can output a field `dimensions` to define the its own output dimensions.
|
|
* We also allow transformers to ignore the output `dimensions` field, and
|
|
* inherit the upstream dimensions definition. It can reduce the burden of handling
|
|
* dimensions in transformers.
|
|
*
|
|
* See also [DIMENSION_INHERIT_RULE] in `sourceManager.ts`.
|
|
*/
|
|
if (firstUpSource && resultIndex === 0
|
|
// If transformer returns `dimensions`, it means that the transformer has different
|
|
// dimensions definitions. We do not inherit anything from upstream.
|
|
&& !result.dimensions) {
|
|
var startIndex = firstUpSource.startIndex;
|
|
// We copy the header of upstream to the result, because:
|
|
// (1) The returned data always does not contain header line and can not be used
|
|
// as dimension-detection. In this case we can not use "detected dimensions" of
|
|
// upstream directly, because it might be detected based on different `seriesLayoutBy`.
|
|
// (2) We should support that the series read the upstream source in `seriesLayoutBy: 'row'`.
|
|
// So the original detected header should be add to the result, otherwise they can not be read.
|
|
if (startIndex) {
|
|
result.data = firstUpSource.data.slice(0, startIndex).concat(result.data);
|
|
}
|
|
resultMetaRawOption = {
|
|
seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN,
|
|
sourceHeader: startIndex,
|
|
dimensions: firstUpSource.metaRawOption.dimensions
|
|
};
|
|
} else {
|
|
resultMetaRawOption = {
|
|
seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN,
|
|
sourceHeader: 0,
|
|
dimensions: result.dimensions
|
|
};
|
|
}
|
|
return createSource(result.data, resultMetaRawOption, null);
|
|
});
|
|
}
|
|
function isSupportedSourceFormat(sourceFormat) {
|
|
return sourceFormat === SOURCE_FORMAT_ARRAY_ROWS || sourceFormat === SOURCE_FORMAT_OBJECT_ROWS;
|
|
} |