import { Topology } from "./topology"; import Graph = Type.Topology.Resolution.Graph import Pipe = Type.Topology.Resolution.Pipe import Pot = Type.Topology.Resolution.Pot import Criteria = Type.Topology.Condition.Criteria import CriteriaTemplate = Type.Topology.Condition.CriteriaTemplate import { Condition, CriteriaTemplateBuilder } from "./condition" import * as CoreLogic from "./coreLogic" import { Errors } from "../errors" export namespace Graph { } export class GraphBuilder { constructor(public readonly topology: Topology) { } name(name: string) { this.result.name = name } result: Graph = { name: null, inputs: [], pots: {}, pipes: {}, } source(name: string): Pot { let src = this.result.pots[name] = { name, type: "source", froms: [], tos: [] } this.result.inputs.push(src.name) return src } hasModel(modelName: string) { let name = GraphBuilder.getModelGraphName(modelName) if (this.result.pots[name]) return true return false } getModelPot(modelName: string) { let name = GraphBuilder.getModelGraphName(modelName) return this.result.pots[name] || null } ensureModelPot(modelName: string) { if (this.hasModel(modelName)) return this.getModelPot(modelName) return this.model(modelName) } model(modelName: string, name = GraphBuilder.getModelGraphName(modelName)): Pot { let model = this.topology.getModel(modelName) if (!model) { throw new Error(`Model ${name} not found`) } return this.result.pots[name] = { name: name, type: "model", modelName: model.name, froms: [], tos: [] } } pipe(option: { from: Pot, to: Pot, criteriaTemplate?: CriteriaTemplate, transform?: Pipe["transform"] }) { let potFrom = option.from let potTo = option.to let name = potFrom.name + "|" + potTo.name if (this.result.pipes[name]) { throw new Error(`Pipe ${name} already exists`) } potFrom.tos.push(name) potTo.froms.push(name) return this.result.pipes[name] = { name, transform: option.transform, criteriaTemplate: option.criteriaTemplate, from: potFrom.name, to: potTo.name } } getPipe(option: { from: Pot, to: Pot }) { let potFrom = option.from let potTo = option.to let name = potFrom.name + "|" + potTo.name return this.result.pipes[name] } ensurePotsByLink(link: Type.Topology.Model.Link) { let first: Pot let pots: Pot[] = [] for (let step of link.steps) { let from = this.ensureModelPot(step.model.from) if (!first) { first = from pots.push(from) } let to = this.ensureModelPot(step.model.to) pots.push(from) let pipe = this.getPipe({ from, to }) let tb = new CriteriaTemplateBuilder let fieldPath = step.joint.to.split(".").slice(1).join(".") let fromPath = step.joint.from.split(".").slice(1).join(".") let criteriaTemplate: Type.Topology.Condition.CriteriaTemplate = tb.path(fieldPath).eq(fromPath).getTemplate() if (pipe) { pipe.criteriaTemplate = Condition.and(pipe.criteriaTemplate, criteriaTemplate) } else { pipe = this.pipe({ from, to, criteriaTemplate }) } } return pots } getSrcGraphName(target: string) { return "src_" + target } sourceOf(option: { name?: string, of: Pot, criteriaTemplate: CriteriaTemplate }) { let srcName = option.name || this.getSrcGraphName(option.of.name) let src = this.source(srcName) let pipe = this.pipe({ from: src, to: option.of, criteriaTemplate: option.criteriaTemplate }) return src } pickLink(option: { start: Type.Topology.Model.Name, end: Type.Topology.Model.Name, vias?: (Type.Topology.Model.Name | Type.Topology.Model.Joint)[][] }) { let start = this.topology.models[option.start] if (!start) { throw new Error("Not found") } let links = start.links[option.end] if (!links || !(links.length > 0)) { throw new Error(`Not link between ${option.start} toward ${option.end}`) } let vias = option.vias && option.vias.filter(item => item.length > 0) || [] let minLength = 0 let candidates: Type.Topology.Model.Link[] = [] for (let link of links) { if (link.steps.length > minLength && minLength > 0) break let checkVias = vias.map(item => item.slice()) for (let step of link.steps) { checkVias = checkVias.filter(vias => { let via = vias[0] if (this.isJointSatisfyVia(step.joint.from, via) || this.isJointSatisfyVia(step.joint.to, via)) { vias.shift() if (vias.length == 0) { return false } } return true }) if (checkVias.length == 0) { candidates.push(link) minLength = link.steps.length break } } } if (candidates.length == 1) return candidates[0] if (candidates.length > 1) { console.error("Ambiguous via", option.vias) console.error(`Pick link from ${option.start} => ${option.end} Has following shortest candidates:`) for (let link of candidates) { CoreLogic.printLink(link) } throw new Errors.LogicError("Ambiguous link resolution") } throw new Error(`Not link between ${option.start} toward ${option.end} satifiying vias}`) } isJointSatisfyVia(joint: string, via: string) { let jointParts = joint.split(".") let viaParts = via.split(".") for (let i = 0; i < viaParts.length; i++) { if (viaParts[i] !== jointParts[i]) { return false } } return true } build() { return this.result } static getModelGraphName(name: Type.Topology.Model.Name): string { return "$m" + name } }