transform.js 16 KB

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