transform.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. /**
  20. * AUTO-GENERATED FILE. DO NOT MODIFY.
  21. */
  22. /*
  23. * Licensed to the Apache Software Foundation (ASF) under one
  24. * or more contributor license agreements. See the NOTICE file
  25. * distributed with this work for additional information
  26. * regarding copyright ownership. The ASF licenses this file
  27. * to you under the Apache License, Version 2.0 (the
  28. * "License"); you may not use this file except in compliance
  29. * with the License. You may obtain a copy of the License at
  30. *
  31. * http://www.apache.org/licenses/LICENSE-2.0
  32. *
  33. * Unless required by applicable law or agreed to in writing,
  34. * software distributed under the License is distributed on an
  35. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36. * KIND, either express or implied. See the License for the
  37. * specific language governing permissions and limitations
  38. * under the License.
  39. */
  40. import { SERIES_LAYOUT_BY_COLUMN, SOURCE_FORMAT_OBJECT_ROWS, SOURCE_FORMAT_ARRAY_ROWS } from '../../util/types.js';
  41. import { normalizeToArray } from '../../util/model.js';
  42. import { createHashMap, bind, each, hasOwn, map, clone, isObject, extend, isNumber } from 'zrender/lib/core/util.js';
  43. import { getRawSourceItemGetter, getRawSourceDataCounter, getRawSourceValueGetter } from './dataProvider.js';
  44. import { parseDataValue } from './dataValueHelper.js';
  45. import { log, makePrintable, throwError } from '../../util/log.js';
  46. import { createSource, detectSourceFormat } from '../Source.js';
  47. /**
  48. * TODO: disable writable.
  49. * This structure will be exposed to users.
  50. */
  51. var ExternalSource = /** @class */function () {
  52. function ExternalSource() {}
  53. ExternalSource.prototype.getRawData = function () {
  54. // Only built-in transform available.
  55. throw new Error('not supported');
  56. };
  57. ExternalSource.prototype.getRawDataItem = function (dataIndex) {
  58. // Only built-in transform available.
  59. throw new Error('not supported');
  60. };
  61. ExternalSource.prototype.cloneRawData = function () {
  62. return;
  63. };
  64. /**
  65. * @return If dimension not found, return null/undefined.
  66. */
  67. ExternalSource.prototype.getDimensionInfo = function (dim) {
  68. return;
  69. };
  70. /**
  71. * dimensions defined if and only if either:
  72. * (a) dataset.dimensions are declared.
  73. * (b) dataset data include dimensions definitions in data (detected or via specified `sourceHeader`).
  74. * If dimensions are defined, `dimensionInfoAll` is corresponding to
  75. * the defined dimensions.
  76. * Otherwise, `dimensionInfoAll` is determined by data columns.
  77. * @return Always return an array (even empty array).
  78. */
  79. ExternalSource.prototype.cloneAllDimensionInfo = function () {
  80. return;
  81. };
  82. ExternalSource.prototype.count = function () {
  83. return;
  84. };
  85. /**
  86. * Only support by dimension index.
  87. * No need to support by dimension name in transform function,
  88. * because transform function is not case-specific, no need to use name literally.
  89. */
  90. ExternalSource.prototype.retrieveValue = function (dataIndex, dimIndex) {
  91. return;
  92. };
  93. ExternalSource.prototype.retrieveValueFromItem = function (dataItem, dimIndex) {
  94. return;
  95. };
  96. ExternalSource.prototype.convertValue = function (rawVal, dimInfo) {
  97. return parseDataValue(rawVal, dimInfo);
  98. };
  99. return ExternalSource;
  100. }();
  101. export { ExternalSource };
  102. function createExternalSource(internalSource, externalTransform) {
  103. var extSource = new ExternalSource();
  104. var data = internalSource.data;
  105. var sourceFormat = extSource.sourceFormat = internalSource.sourceFormat;
  106. var sourceHeaderCount = internalSource.startIndex;
  107. var errMsg = '';
  108. if (internalSource.seriesLayoutBy !== SERIES_LAYOUT_BY_COLUMN) {
  109. // For the logic simplicity in transformer, only 'culumn' is
  110. // supported in data transform. Otherwise, the `dimensionsDefine`
  111. // might be detected by 'row', which probably confuses users.
  112. if (process.env.NODE_ENV !== 'production') {
  113. errMsg = '`seriesLayoutBy` of upstream dataset can only be "column" in data transform.';
  114. }
  115. throwError(errMsg);
  116. }
  117. // [MEMO]
  118. // Create a new dimensions structure for exposing.
  119. // Do not expose all dimension info to users directly.
  120. // Because the dimension is probably auto detected from data and not might reliable.
  121. // Should not lead the transformers to think that is reliable and return it.
  122. // See [DIMENSION_INHERIT_RULE] in `sourceManager.ts`.
  123. var dimensions = [];
  124. var dimsByName = {};
  125. var dimsDef = internalSource.dimensionsDefine;
  126. if (dimsDef) {
  127. each(dimsDef, function (dimDef, idx) {
  128. var name = dimDef.name;
  129. var dimDefExt = {
  130. index: idx,
  131. name: name,
  132. displayName: dimDef.displayName
  133. };
  134. dimensions.push(dimDefExt);
  135. // Users probably do not specify dimension name. For simplicity, data transform
  136. // does not generate dimension name.
  137. if (name != null) {
  138. // Dimension name should not be duplicated.
  139. // For simplicity, data transform forbids name duplication, do not generate
  140. // new name like module `completeDimensions.ts` did, but just tell users.
  141. var errMsg_1 = '';
  142. if (hasOwn(dimsByName, name)) {
  143. if (process.env.NODE_ENV !== 'production') {
  144. errMsg_1 = 'dimension name "' + name + '" duplicated.';
  145. }
  146. throwError(errMsg_1);
  147. }
  148. dimsByName[name] = dimDefExt;
  149. }
  150. });
  151. }
  152. // If dimension definitions are not defined and can not be detected.
  153. // e.g., pure data `[[11, 22], ...]`.
  154. else {
  155. for (var i = 0; i < internalSource.dimensionsDetectedCount || 0; i++) {
  156. // Do not generete name or anything others. The consequence process in
  157. // `transform` or `series` probably have there own name generation strategry.
  158. dimensions.push({
  159. index: i
  160. });
  161. }
  162. }
  163. // Implement public methods:
  164. var rawItemGetter = getRawSourceItemGetter(sourceFormat, SERIES_LAYOUT_BY_COLUMN);
  165. if (externalTransform.__isBuiltIn) {
  166. extSource.getRawDataItem = function (dataIndex) {
  167. return rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex);
  168. };
  169. extSource.getRawData = bind(getRawData, null, internalSource);
  170. }
  171. extSource.cloneRawData = bind(cloneRawData, null, internalSource);
  172. var rawCounter = getRawSourceDataCounter(sourceFormat, SERIES_LAYOUT_BY_COLUMN);
  173. extSource.count = bind(rawCounter, null, data, sourceHeaderCount, dimensions);
  174. var rawValueGetter = getRawSourceValueGetter(sourceFormat);
  175. extSource.retrieveValue = function (dataIndex, dimIndex) {
  176. var rawItem = rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex);
  177. return retrieveValueFromItem(rawItem, dimIndex);
  178. };
  179. var retrieveValueFromItem = extSource.retrieveValueFromItem = function (dataItem, dimIndex) {
  180. if (dataItem == null) {
  181. return;
  182. }
  183. var dimDef = dimensions[dimIndex];
  184. // When `dimIndex` is `null`, `rawValueGetter` return the whole item.
  185. if (dimDef) {
  186. return rawValueGetter(dataItem, dimIndex, dimDef.name);
  187. }
  188. };
  189. extSource.getDimensionInfo = bind(getDimensionInfo, null, dimensions, dimsByName);
  190. extSource.cloneAllDimensionInfo = bind(cloneAllDimensionInfo, null, dimensions);
  191. return extSource;
  192. }
  193. function getRawData(upstream) {
  194. var sourceFormat = upstream.sourceFormat;
  195. if (!isSupportedSourceFormat(sourceFormat)) {
  196. var errMsg = '';
  197. if (process.env.NODE_ENV !== 'production') {
  198. errMsg = '`getRawData` is not supported in source format ' + sourceFormat;
  199. }
  200. throwError(errMsg);
  201. }
  202. return upstream.data;
  203. }
  204. function cloneRawData(upstream) {
  205. var sourceFormat = upstream.sourceFormat;
  206. var data = upstream.data;
  207. if (!isSupportedSourceFormat(sourceFormat)) {
  208. var errMsg = '';
  209. if (process.env.NODE_ENV !== 'production') {
  210. errMsg = '`cloneRawData` is not supported in source format ' + sourceFormat;
  211. }
  212. throwError(errMsg);
  213. }
  214. if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {
  215. var result = [];
  216. for (var i = 0, len = data.length; i < len; i++) {
  217. // Not strictly clone for performance
  218. result.push(data[i].slice());
  219. }
  220. return result;
  221. } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {
  222. var result = [];
  223. for (var i = 0, len = data.length; i < len; i++) {
  224. // Not strictly clone for performance
  225. result.push(extend({}, data[i]));
  226. }
  227. return result;
  228. }
  229. }
  230. function getDimensionInfo(dimensions, dimsByName, dim) {
  231. if (dim == null) {
  232. return;
  233. }
  234. // Keep the same logic as `List::getDimension` did.
  235. if (isNumber(dim)
  236. // If being a number-like string but not being defined a dimension name.
  237. || !isNaN(dim) && !hasOwn(dimsByName, dim)) {
  238. return dimensions[dim];
  239. } else if (hasOwn(dimsByName, dim)) {
  240. return dimsByName[dim];
  241. }
  242. }
  243. function cloneAllDimensionInfo(dimensions) {
  244. return clone(dimensions);
  245. }
  246. var externalTransformMap = createHashMap();
  247. export function registerExternalTransform(externalTransform) {
  248. externalTransform = clone(externalTransform);
  249. var type = externalTransform.type;
  250. var errMsg = '';
  251. if (!type) {
  252. if (process.env.NODE_ENV !== 'production') {
  253. errMsg = 'Must have a `type` when `registerTransform`.';
  254. }
  255. throwError(errMsg);
  256. }
  257. var typeParsed = type.split(':');
  258. if (typeParsed.length !== 2) {
  259. if (process.env.NODE_ENV !== 'production') {
  260. errMsg = 'Name must include namespace like "ns:regression".';
  261. }
  262. throwError(errMsg);
  263. }
  264. // Namespace 'echarts:xxx' is official namespace, where the transforms should
  265. // be called directly via 'xxx' rather than 'echarts:xxx'.
  266. var isBuiltIn = false;
  267. if (typeParsed[0] === 'echarts') {
  268. type = typeParsed[1];
  269. isBuiltIn = true;
  270. }
  271. externalTransform.__isBuiltIn = isBuiltIn;
  272. externalTransformMap.set(type, externalTransform);
  273. }
  274. export function applyDataTransform(rawTransOption, sourceList, infoForPrint) {
  275. var pipedTransOption = normalizeToArray(rawTransOption);
  276. var pipeLen = pipedTransOption.length;
  277. var errMsg = '';
  278. if (!pipeLen) {
  279. if (process.env.NODE_ENV !== 'production') {
  280. errMsg = 'If `transform` declared, it should at least contain one transform.';
  281. }
  282. throwError(errMsg);
  283. }
  284. for (var i = 0, len = pipeLen; i < len; i++) {
  285. var transOption = pipedTransOption[i];
  286. sourceList = applySingleDataTransform(transOption, sourceList, infoForPrint, pipeLen === 1 ? null : i);
  287. // piped transform only support single input, except the fist one.
  288. // piped transform only support single output, except the last one.
  289. if (i !== len - 1) {
  290. sourceList.length = Math.max(sourceList.length, 1);
  291. }
  292. }
  293. return sourceList;
  294. }
  295. function applySingleDataTransform(transOption, upSourceList, infoForPrint,
  296. // If `pipeIndex` is null/undefined, no piped transform.
  297. pipeIndex) {
  298. var errMsg = '';
  299. if (!upSourceList.length) {
  300. if (process.env.NODE_ENV !== 'production') {
  301. errMsg = 'Must have at least one upstream dataset.';
  302. }
  303. throwError(errMsg);
  304. }
  305. if (!isObject(transOption)) {
  306. if (process.env.NODE_ENV !== 'production') {
  307. errMsg = 'transform declaration must be an object rather than ' + typeof transOption + '.';
  308. }
  309. throwError(errMsg);
  310. }
  311. var transType = transOption.type;
  312. var externalTransform = externalTransformMap.get(transType);
  313. if (!externalTransform) {
  314. if (process.env.NODE_ENV !== 'production') {
  315. errMsg = 'Can not find transform on type "' + transType + '".';
  316. }
  317. throwError(errMsg);
  318. }
  319. // Prepare source
  320. var extUpSourceList = map(upSourceList, function (upSource) {
  321. return createExternalSource(upSource, externalTransform);
  322. });
  323. var resultList = normalizeToArray(externalTransform.transform({
  324. upstream: extUpSourceList[0],
  325. upstreamList: extUpSourceList,
  326. config: clone(transOption.config)
  327. }));
  328. if (process.env.NODE_ENV !== 'production') {
  329. if (transOption.print) {
  330. var printStrArr = map(resultList, function (extSource) {
  331. var pipeIndexStr = pipeIndex != null ? ' === pipe index: ' + pipeIndex : '';
  332. return ['=== dataset index: ' + infoForPrint.datasetIndex + pipeIndexStr + ' ===', '- transform result data:', makePrintable(extSource.data), '- transform result dimensions:', makePrintable(extSource.dimensions)].join('\n');
  333. }).join('\n');
  334. log(printStrArr);
  335. }
  336. }
  337. return map(resultList, function (result, resultIndex) {
  338. var errMsg = '';
  339. if (!isObject(result)) {
  340. if (process.env.NODE_ENV !== 'production') {
  341. errMsg = 'A transform should not return some empty results.';
  342. }
  343. throwError(errMsg);
  344. }
  345. if (!result.data) {
  346. if (process.env.NODE_ENV !== 'production') {
  347. errMsg = 'Transform result data should be not be null or undefined';
  348. }
  349. throwError(errMsg);
  350. }
  351. var sourceFormat = detectSourceFormat(result.data);
  352. if (!isSupportedSourceFormat(sourceFormat)) {
  353. if (process.env.NODE_ENV !== 'production') {
  354. errMsg = 'Transform result data should be array rows or object rows.';
  355. }
  356. throwError(errMsg);
  357. }
  358. var resultMetaRawOption;
  359. var firstUpSource = upSourceList[0];
  360. /**
  361. * Intuitively, the end users known the content of the original `dataset.source`,
  362. * calucating the transform result in mind.
  363. * Suppose the original `dataset.source` is:
  364. * ```js
  365. * [
  366. * ['product', '2012', '2013', '2014', '2015'],
  367. * ['AAA', 41.1, 30.4, 65.1, 53.3],
  368. * ['BBB', 86.5, 92.1, 85.7, 83.1],
  369. * ['CCC', 24.1, 67.2, 79.5, 86.4]
  370. * ]
  371. * ```
  372. * The dimension info have to be detected from the source data.
  373. * Some of the transformers (like filter, sort) will follow the dimension info
  374. * of upstream, while others use new dimensions (like aggregate).
  375. * Transformer can output a field `dimensions` to define the its own output dimensions.
  376. * We also allow transformers to ignore the output `dimensions` field, and
  377. * inherit the upstream dimensions definition. It can reduce the burden of handling
  378. * dimensions in transformers.
  379. *
  380. * See also [DIMENSION_INHERIT_RULE] in `sourceManager.ts`.
  381. */
  382. if (firstUpSource && resultIndex === 0
  383. // If transformer returns `dimensions`, it means that the transformer has different
  384. // dimensions definitions. We do not inherit anything from upstream.
  385. && !result.dimensions) {
  386. var startIndex = firstUpSource.startIndex;
  387. // We copy the header of upstream to the result, because:
  388. // (1) The returned data always does not contain header line and can not be used
  389. // as dimension-detection. In this case we can not use "detected dimensions" of
  390. // upstream directly, because it might be detected based on different `seriesLayoutBy`.
  391. // (2) We should support that the series read the upstream source in `seriesLayoutBy: 'row'`.
  392. // So the original detected header should be add to the result, otherwise they can not be read.
  393. if (startIndex) {
  394. result.data = firstUpSource.data.slice(0, startIndex).concat(result.data);
  395. }
  396. resultMetaRawOption = {
  397. seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN,
  398. sourceHeader: startIndex,
  399. dimensions: firstUpSource.metaRawOption.dimensions
  400. };
  401. } else {
  402. resultMetaRawOption = {
  403. seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN,
  404. sourceHeader: 0,
  405. dimensions: result.dimensions
  406. };
  407. }
  408. return createSource(result.data, resultMetaRawOption, null);
  409. });
  410. }
  411. function isSupportedSourceFormat(sourceFormat) {
  412. return sourceFormat === SOURCE_FORMAT_ARRAY_ROWS || sourceFormat === SOURCE_FORMAT_OBJECT_ROWS;
  413. }