import { Util } from "../Common/util"; import { Engine } from "../engine"; import { ElementOption, Layouter, LayoutGroupOptions, LinkOption, PointerOption } from "../options"; import { sourceLinkData, SourceElement, LinkTarget, Sources } from "../sources"; import { SV } from "../StructV"; import { Element, Link, Model, Pointer } from "./modelData"; export type LayoutGroup = { element: Element[]; link: Link[]; pointer: Pointer[]; layouter: Layouter; layouterName: string; options: LayoutGroupOptions; modelList: Model[]; isHide: boolean; }; export type LayoutGroupTable = Map; export class ModelConstructor { private engine: Engine; private layoutGroupTable: LayoutGroupTable; private prevSourcesStringMap: { [key: string]: string }; // 保存上一次源数据转换为字符串之后的值,用作比较该次源数据和上一次源数据是否有差异,若相同,则可跳过重复构建过程 constructor(engine: Engine) { this.engine = engine; this.prevSourcesStringMap = { }; } /** * 构建element,link和pointer * @param sourceList */ public construct(sources: Sources): LayoutGroupTable { const layoutGroupTable = new Map(), layouterMap: { [key: string]: Layouter } = SV.registeredLayouter, optionsTable = this.engine.optionsTable; Object.keys(sources).forEach(name => { let sourceGroup = sources[name], layouterName = sourceGroup.layouter, layouter: Layouter = layouterMap[sourceGroup.layouter]; if(!layouterName || !layouter) { return; } let sourceDataString: string = JSON.stringify(sourceGroup.data), prevString: string = this.prevSourcesStringMap[name], elementList: Element[] = [], pointerList: Pointer[] = []; if(prevString === sourceDataString) { return; } const options: LayoutGroupOptions = optionsTable[layouterName], sourceData = layouter.sourcesPreprocess(sourceGroup.data), elementOptions = options.element || { }, pointerOptions = options.pointer || { }; elementList = this.constructElements(elementOptions, name, sourceData, layouterName); pointerList = this.constructPointers(pointerOptions, elementList); layoutGroupTable.set(name, { element: elementList, link: [], pointer: pointerList, options: options, layouter: layouter, modelList: [...elementList, ...pointerList], layouterName, isHide: false }); }); layoutGroupTable.forEach((layoutGroup: LayoutGroup) => { const linkOptions = layoutGroup.options.link || { }, linkList: Link[] = this.constructLinks(linkOptions, layoutGroup.element, layoutGroupTable); layoutGroup.link = linkList; layoutGroup.modelList.push(...linkList); }); this.layoutGroupTable = layoutGroupTable; return this.layoutGroupTable; } /** * * @returns */ public getLayoutGroupTable(): LayoutGroupTable { return this.layoutGroupTable; } /** * 从源数据构建 element 集 * @param elementOptions * @param groupName * @param sourceList * @param layouterName * @returns */ private constructElements(elementOptions: { [key: string]: ElementOption }, groupName: string, sourceList: SourceElement[], layouterName: string): Element[] { let defaultElementType: string = 'default', elementList: Element[] = []; sourceList.forEach(item => { if(item === null) { return; } if(item.type === undefined || item.type === null) { item.type = defaultElementType; } elementList.push(this.createElement(item, item.type, groupName, layouterName, elementOptions[item.type])); }); return elementList; } /** * 从配置和 element 集构建 link 集 * @param linkOptions * @param elements * @param layoutGroupTable * @returns */ private constructLinks(linkOptions: { [key: string]: LinkOption }, elements: Element[], layoutGroupTable: LayoutGroupTable): Link[] { let linkList: Link[] = [], linkNames = Object.keys(linkOptions); linkNames.forEach(name => { for(let i = 0; i < elements.length; i++) { let element: Element = elements[i], sourceLinkData: sourceLinkData = element.sourceElement[name], targetElement: Element | Element[] = null, link: Link = null; if(sourceLinkData === undefined || sourceLinkData === null) { element[name] = null; continue; } // ------------------- 将连接声明字段 sourceLinkData 从 id 变为 Element ------------------- if(Array.isArray(sourceLinkData)) { element[name] = sourceLinkData.map((item, index) => { targetElement = this.fetchTargetElements(layoutGroupTable, element, item); if(targetElement) { link = this.createLink(name, element, targetElement, index, linkOptions[name]); linkList.push(link); } return targetElement; }); } else { targetElement = this.fetchTargetElements(layoutGroupTable, element, sourceLinkData); if(targetElement) { link = this.createLink(name, element, targetElement, null, linkOptions[name]); linkList.push(link); } element[name] = targetElement; } } }); return linkList; } /** * 从配置和 element 集构建 pointer 集 * @param pointerOptions * @param elements * @returns */ private constructPointers(pointerOptions: { [key: string]: PointerOption }, elements: Element[]): Pointer[] { let pointerList: Pointer[] = [], pointerNames = Object.keys(pointerOptions); pointerNames.forEach(name => { for(let i = 0; i < elements.length; i++) { let element = elements[i], pointerData = element[name]; // 若没有指针字段的结点则跳过 if(!pointerData) continue; let id = name + '.' + (Array.isArray(pointerData)? pointerData.join('-'): pointerData), pointer = this.createPointer(id, name, pointerData, element, pointerOptions[name]); pointerList.push(pointer); } }); return pointerList; } /** * 元素工厂,创建Element * @param sourceElement * @param elementName * @param groupName * @param layouterName * @param options */ private createElement(sourceElement: SourceElement, elementName: string, groupName: string, layouterName: string, options: ElementOption): Element { let element: Element = undefined, label: string | string[] = '', id = elementName + '.' + sourceElement.id.toString(); if(options.label) { if(Array.isArray(options.label)) { label = options.label.map(item => { let res = this.parserElementContent(sourceElement, item); if(res === null || label === 'undefined') { return ''; } return res; }); } else { label = this.parserElementContent(sourceElement, options.label); if(label === null || label === 'undefined') { label = ''; } } } element = new Element(id, elementName, groupName, layouterName, sourceElement); element.initProps(options); element.set('label', label); element.sourceElement = sourceElement; return element; } /** * 外部指针工厂,创建Pointer * @param id * @param pointerName * @param label * @param target * @param options */ private createPointer(id: string, pointerName: string, pointerData: string | string[], target: Element, options: PointerOption): Pointer { let pointer = undefined; pointer = new Pointer(id, pointerName, pointerData, target); pointer.initProps(options); return pointer; }; /** * 连线工厂,创建Link * @param linkName * @param element * @param target * @param index * @param options */ private createLink(linkName: string, element: Element, target: Element, index: number, options: LinkOption): Link { let link = undefined, id = `${element.id}-${target.id}`; link = new Link(id, linkName, element, target, index); link.initProps(options); return link; } /** * 解析元素文本内容 * @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(layoutGroupTable: LayoutGroupTable, element: Element, linkTarget: LinkTarget): Element { let groupName: string = element.groupName, elementName = element.type, elementList: Element[], targetId = linkTarget, targetGroupName = groupName, targetElement = null; if(linkTarget === null || linkTarget === undefined) { return null; } if(typeof linkTarget === 'number' || (typeof linkTarget === 'string' && !linkTarget.includes('#'))) { linkTarget = 'default#' + linkTarget; } let info = linkTarget.split('#'); targetId = info.pop(); if(info.length > 1) { elementName = info.pop(); targetGroupName = info.pop(); } else { let field = info.pop(); if(layoutGroupTable.get(targetGroupName).element.find(item => item.type === field)) { elementName = field; } else if(layoutGroupTable.has(field)) { targetGroupName = field; } else { return null; } } elementList = layoutGroupTable.get(targetGroupName).element.filter(item => item.type === elementName); // 若目标element不存在,返回null if(elementList === undefined) { return null; } targetElement = elementList.find(item => item.sourceId === targetId); return targetElement || null; } /** * 销毁 */ destroy() { this.layoutGroupTable = null; } };