243 lines
8.1 KiB
TypeScript
243 lines
8.1 KiB
TypeScript
import { Util } from "../Common/util";
|
||
import { Engine } from "../engine";
|
||
import { LinkOption, PointerOption } from "../options";
|
||
import { sourceLinkData, SourceElement, Sources, LinkTarget } from "../sources";
|
||
import { Element, Link, Pointer } from "./modelData";
|
||
|
||
|
||
export interface ConstructedData {
|
||
element: { [key: string]: Element[] };
|
||
link: { [key: string]: Link[] };
|
||
pointer: { [key: string]: Pointer[] };
|
||
};
|
||
|
||
|
||
export class ModelConstructor {
|
||
private engine: Engine;
|
||
|
||
constructor(engine: Engine) {
|
||
this.engine = engine;
|
||
}
|
||
|
||
/**
|
||
* 构建element,link和pointer
|
||
* @param sourceData
|
||
*/
|
||
public construct(sourceData: Sources): ConstructedData {
|
||
let elementContainer = this.constructElements(sourceData),
|
||
linkContainer = this.constructLinks(this.engine.linkOptions, elementContainer),
|
||
pointerContainer = this.constructPointers(this.engine.pointerOptions, elementContainer);
|
||
|
||
return {
|
||
element: elementContainer,
|
||
link: linkContainer,
|
||
pointer: pointerContainer
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 从源数据构建 element 集
|
||
* @param sourceData
|
||
*/
|
||
private constructElements(sourceData: Sources): { [key: string]: Element[] } {
|
||
let defaultElementName: string = 'default',
|
||
elementContainer: { [key: string]: Element[] } = { };
|
||
|
||
if(Array.isArray(sourceData)) {
|
||
elementContainer[defaultElementName] = [];
|
||
sourceData.forEach(item => {
|
||
if(item) {
|
||
let ele = this.createElement(item, defaultElementName);
|
||
elementContainer[defaultElementName].push(ele);
|
||
}
|
||
});
|
||
}
|
||
else {
|
||
Object.keys(sourceData).forEach(prop => {
|
||
elementContainer[prop] = [];
|
||
sourceData[prop].forEach(item => {
|
||
if(item) {
|
||
let element = this.createElement(item, prop);
|
||
elementContainer[prop].push(element);
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
return elementContainer;
|
||
}
|
||
|
||
/**
|
||
* 从配置和 element 集构建 link 集
|
||
* @param linkOptions
|
||
* @param elementContainer
|
||
* @returns
|
||
*/
|
||
private constructLinks(linkOptions: { [key: string]: LinkOption }, elementContainer: { [key: string]: Element[] }): { [key: string]: Link[] } {
|
||
let linkContainer: { [key: string]: Link[] } = { },
|
||
elementList: Element[] = Object
|
||
.keys(elementContainer)
|
||
.map(item => elementContainer[item])
|
||
.reduce((prev, cur) => [...prev, ...cur]),
|
||
linkNames = Object.keys(linkOptions);
|
||
|
||
linkNames.forEach(name => {
|
||
linkContainer[name] = [];
|
||
});
|
||
|
||
linkNames.forEach(name => {
|
||
for(let i = 0; i < elementList.length; i++) {
|
||
let element: Element = elementList[i],
|
||
sourceLinkData: sourceLinkData = element[name],
|
||
options: LinkOption = linkOptions[name],
|
||
targetElement: Element | Element[] = null,
|
||
link: Link = null;
|
||
|
||
if(sourceLinkData === undefined || sourceLinkData === null) continue;
|
||
|
||
// ------------------- 将连接声明字段 sourceLinkData 从 id 变为 Element -------------------
|
||
if(Array.isArray(sourceLinkData)) {
|
||
element[name] = sourceLinkData.map((item, index) => {
|
||
targetElement = this.fetchTargetElements(elementContainer, element, item);
|
||
|
||
if(targetElement) {
|
||
link = new Link(name, element, targetElement, index);
|
||
linkContainer[name].push(link);
|
||
link.initProps(options);
|
||
}
|
||
|
||
return targetElement;
|
||
});
|
||
}
|
||
else {
|
||
targetElement = this.fetchTargetElements(elementContainer, element, sourceLinkData);
|
||
|
||
if(targetElement) {
|
||
link = new Link(name, element, targetElement, null);
|
||
linkContainer[name].push(link);
|
||
link.initProps(options);
|
||
}
|
||
|
||
element[name] = targetElement;
|
||
}
|
||
}
|
||
});
|
||
|
||
return linkContainer;
|
||
}
|
||
|
||
/**
|
||
* 从配置和 element 集构建 pointer 集
|
||
* @param pointerOptions
|
||
* @param elementContainer
|
||
* @returns
|
||
*/
|
||
private constructPointers(pointerOptions: { [key: string]: PointerOption }, elementContainer: { [key: string]: Element[] }): { [key: string]: Pointer[] } {
|
||
let pointerContainer: { [key: string]: Pointer[] } = { },
|
||
elementList: Element[] = Object
|
||
.keys(elementContainer)
|
||
.map(item => elementContainer[item])
|
||
.reduce((prev, cur) => [...prev, ...cur]),
|
||
pointerNames = Object.keys(pointerOptions);
|
||
|
||
pointerNames.forEach(name => {
|
||
pointerContainer[name] = [];
|
||
});
|
||
|
||
pointerNames.forEach(name => {
|
||
let options = pointerOptions[name];
|
||
|
||
for(let i = 0; i < elementList.length; i++) {
|
||
let element = elementList[i],
|
||
pointerData = element[name];
|
||
|
||
// 若没有指针字段的结点则跳过
|
||
if(!pointerData) continue;
|
||
|
||
let id = name + '#' + (Array.isArray(pointerData)? pointerData.join('-'): pointerData),
|
||
pointer = new Pointer(id, name, pointerData, element);
|
||
|
||
pointer.initProps(options);
|
||
pointerContainer[name].push(pointer);
|
||
}
|
||
});
|
||
|
||
return pointerContainer;
|
||
}
|
||
|
||
/**
|
||
* 元素工厂,创建Element
|
||
* @param sourceElement
|
||
* @param elementName
|
||
*/
|
||
private createElement(sourceElement: SourceElement, elementName: string): Element {
|
||
let elementOption = this.engine.elementOptions[elementName],
|
||
element = new Element(elementName, sourceElement),
|
||
label = elementOption.label? this.parserElementContent(sourceElement, elementOption.label): '';
|
||
|
||
element.initProps(elementOption);
|
||
element.set('label', label);
|
||
|
||
return element;
|
||
}
|
||
|
||
/**
|
||
* 解析元素文本内容
|
||
* @param sourceElement
|
||
* @param formatLabel
|
||
*/
|
||
private parserElementContent(sourceElement: SourceElement, formatLabel: string): string {
|
||
let fields = Util.textParser(formatLabel);
|
||
|
||
if(Array.isArray(fields)) {
|
||
let values = fields.map(item => sourceElement[item]);
|
||
|
||
values.map((item, index) => {
|
||
formatLabel = formatLabel.replace('[' + fields[index] + ']', item);
|
||
});
|
||
}
|
||
|
||
return formatLabel;
|
||
}
|
||
|
||
/**
|
||
* 由source中的连接字段获取真实的连接目标元素
|
||
* @param elementContainer
|
||
* @param element
|
||
* @param linkTarget
|
||
*/
|
||
private fetchTargetElements(
|
||
elementContainer: { [key: string]: Element[] } ,
|
||
element: Element,
|
||
linkTarget: LinkTarget
|
||
): Element {
|
||
let elementName = element.name,
|
||
elementList: Element[],
|
||
targetId = linkTarget,
|
||
targetElement = null;
|
||
|
||
if(linkTarget === null || linkTarget === undefined) {
|
||
return null;
|
||
}
|
||
|
||
if(typeof linkTarget === 'string' && linkTarget.includes('#')) {
|
||
let info = linkTarget.split('#');
|
||
elementName = info[0];
|
||
targetId = info[1];
|
||
}
|
||
|
||
if(typeof targetId === 'number') {
|
||
targetId = targetId.toString();
|
||
}
|
||
|
||
elementList = elementContainer[elementName];
|
||
|
||
// 若目标element不存在,返回null
|
||
if(elementList === undefined) {
|
||
return null;
|
||
}
|
||
|
||
targetElement = elementList.find(item => item.id === targetId);
|
||
return targetElement || null;
|
||
}
|
||
}; |