Spaces:
Running
Running
/* | |
* 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. | |
*/ | |
/** | |
* Link lists and struct (graph or tree) | |
*/ | |
import { curry, each, assert, extend, map, keys } from 'zrender/src/core/util'; | |
import SeriesData from '../SeriesData'; | |
import { makeInner } from '../../util/model'; | |
import { SeriesDataType } from '../../util/types'; | |
// That is: { dataType: data }, | |
// like: { node: nodeList, edge: edgeList }. | |
// Should contain mainData. | |
type Datas = { [key in SeriesDataType]?: SeriesData }; | |
type StructReferDataAttr = 'data' | 'edgeData'; | |
type StructAttr = 'tree' | 'graph'; | |
const inner = makeInner<{ | |
datas: Datas; | |
mainData: SeriesData; | |
}, SeriesData>(); | |
// Caution: | |
// In most case, either seriesData or its shallow clones (see seriesData.cloneShallow) | |
// is active in echarts process. So considering heap memory consumption, | |
// we do not clone tree or graph, but share them among seriesData and its shallow clones. | |
// But in some rare case, we have to keep old seriesData (like do animation in chart). So | |
// please take care that both the old seriesData and the new seriesData share the same tree/graph. | |
type LinkSeriesDataOpt = { | |
mainData: SeriesData; | |
// For example, instance of Graph or Tree. | |
struct: { | |
update: () => void; | |
} & { | |
[key in StructReferDataAttr]?: SeriesData | |
}; | |
// Will designate: `mainData[structAttr] = struct;` | |
structAttr: StructAttr; | |
datas?: Datas; | |
// { dataType: attr }, | |
// Will designate: `struct[datasAttr[dataType]] = list;` | |
datasAttr?: { [key in SeriesDataType]?: StructReferDataAttr }; | |
}; | |
function linkSeriesData(opt: LinkSeriesDataOpt): void { | |
const mainData = opt.mainData; | |
let datas = opt.datas; | |
if (!datas) { | |
datas = { main: mainData }; | |
opt.datasAttr = { main: 'data' }; | |
} | |
opt.datas = opt.mainData = null; | |
linkAll(mainData, datas, opt); | |
// Porxy data original methods. | |
each(datas, function (data: SeriesData) { | |
each(mainData.TRANSFERABLE_METHODS, function (methodName) { | |
data.wrapMethod(methodName, curry(transferInjection, opt)); | |
}); | |
}); | |
// Beyond transfer, additional features should be added to `cloneShallow`. | |
mainData.wrapMethod('cloneShallow', curry(cloneShallowInjection, opt)); | |
// Only mainData trigger change, because struct.update may trigger | |
// another changable methods, which may bring about dead lock. | |
each(mainData.CHANGABLE_METHODS, function (methodName) { | |
mainData.wrapMethod(methodName, curry(changeInjection, opt)); | |
}); | |
// Make sure datas contains mainData. | |
assert(datas[mainData.dataType] === mainData); | |
} | |
function transferInjection(this: SeriesData, opt: LinkSeriesDataOpt, res: SeriesData): unknown { | |
if (isMainData(this)) { | |
// Transfer datas to new main data. | |
const datas = extend({}, inner(this).datas); | |
datas[this.dataType] = res; | |
linkAll(res, datas, opt); | |
} | |
else { | |
// Modify the reference in main data to point newData. | |
linkSingle(res, this.dataType, inner(this).mainData, opt); | |
} | |
return res; | |
} | |
function changeInjection(opt: LinkSeriesDataOpt, res: unknown): unknown { | |
opt.struct && opt.struct.update(); | |
return res; | |
} | |
function cloneShallowInjection(opt: LinkSeriesDataOpt, res: SeriesData): SeriesData { | |
// cloneShallow, which brings about some fragilities, may be inappropriate | |
// to be exposed as an API. So for implementation simplicity we can make | |
// the restriction that cloneShallow of not-mainData should not be invoked | |
// outside, but only be invoked here. | |
each(inner(res).datas, function (data: SeriesData, dataType) { | |
data !== res && linkSingle(data.cloneShallow(), dataType, res, opt); | |
}); | |
return res; | |
} | |
/** | |
* Supplement method to List. | |
* | |
* @public | |
* @param [dataType] If not specified, return mainData. | |
*/ | |
function getLinkedData(this: SeriesData, dataType?: SeriesDataType): SeriesData { | |
const mainData = inner(this).mainData; | |
return (dataType == null || mainData == null) | |
? mainData | |
: inner(mainData).datas[dataType]; | |
} | |
/** | |
* Get list of all linked data | |
*/ | |
function getLinkedDataAll(this: SeriesData): { | |
data: SeriesData, | |
type?: SeriesDataType | |
}[] { | |
const mainData = inner(this).mainData; | |
return (mainData == null) | |
? [{ data: mainData }] | |
: map(keys(inner(mainData).datas), function (type) { | |
return { | |
type, | |
data: inner(mainData).datas[type] | |
}; | |
}); | |
} | |
function isMainData(data: SeriesData): boolean { | |
return inner(data).mainData === data; | |
} | |
function linkAll(mainData: SeriesData, datas: Datas, opt: LinkSeriesDataOpt): void { | |
inner(mainData).datas = {}; | |
each(datas, function (data: SeriesData, dataType) { | |
linkSingle(data, dataType, mainData, opt); | |
}); | |
} | |
function linkSingle(data: SeriesData, dataType: SeriesDataType, mainData: SeriesData, opt: LinkSeriesDataOpt): void { | |
inner(mainData).datas[dataType] = data; | |
inner(data).mainData = mainData; | |
data.dataType = dataType; | |
if (opt.struct) { | |
data[opt.structAttr] = opt.struct as any; | |
opt.struct[opt.datasAttr[dataType]] = data; | |
} | |
// Supplement method. | |
data.getLinkedData = getLinkedData; | |
data.getLinkedDataAll = getLinkedDataAll; | |
} | |
export default linkSeriesData; | |