@import org.silkframework.rule.Operator @import org.silkframework.rule.input.TransformInput @import org.silkframework.rule.input.PathInput @import org.silkframework.rule.similarity.Aggregation @import org.silkframework.rule.similarity.Comparison @import org.silkframework.rule.RuleTraverser @import org.silkframework.runtime.plugin.PluginDescription @import views.editor.GenerateId @import org.silkframework.workspace.Project @import org.silkframework.runtime.activity.UserContext @import org.silkframework.runtime.plugin.PluginContext @(rule: org.silkframework.rule.Operator, project: Project)(implicit userContext: UserContext) @drawLevel(Seq(rule), (countLevels(rule) - 1) * 250, maxWidth(rule) * 200) @connectOperators(rule) @drawLevel(ops: Seq[Operator], x: Int, height: Int) = { @if(!ops.isEmpty) { @for((op, index) <- ops.zipWithIndex) { @drawOperator(op, x + 20, height * (index + 1) / (ops.size + 1) - 80) } @drawLevel(ops.flatMap(getChildren), x - 250, height) } } @countLevels(op: Operator) = @{ def count(op: Operator): Int = { val children = getChildren(op) if(children.isEmpty) 1 else 1 + children.map(count).max } count(op) } @maxWidth(op: Operator) = @{ def findMax(ops: Seq[Operator]): Int = { val children = ops.flatMap(getChildren) if(children.isEmpty) ops.size else Seq(ops.size, findMax(children)).max } findMax(Seq(op)) } @getChildren(op: Operator) = @{ op match { case agg: Aggregation => agg.operators case cmp: Comparison => cmp.inputs.toSeq case transform: TransformInput => transform.inputs case path: PathInput => Seq.empty } } @drawOperator(op: Operator, x: Int, y: Int) = { @op match { case Aggregation(id, weight, aggregator, operators) => { @aggregationBox(id, weight, aggregator.pluginSpec, parameterValues(aggregator, aggregator.pluginSpec), x, y, true, project) } case Comparison(id, weight, threshold, indexing, metric, inputs) => { @comparisonBox(id, weight, threshold, metric.pluginSpec, parameterValues(metric, metric.pluginSpec), x, y, true, project) } case TransformInput(id, transformer, inputs) => { @transformationBox(id, transformer.pluginSpec, parameterValues(transformer, transformer.pluginSpec), x, y, true, project) } case PathInput(id, path) => { @pathBox(id, isSourceInput(op), path.serialize()(project.config.prefixes), x, y, true, project) } } } @parameterValues(plugin: AnyRef, pluginType: PluginDescription[_]) = @{ for(p <- pluginType.parameters) yield p.stringValue(plugin)(PluginContext.fromProject(project)) } @** * Determines if an operator is a source or target input. *@ @isSourceInput(op: Operator) = @{ var isSource = true for { // Get a traversable node for the given operator inputNode <- RuleTraverser(rule).iterateAllChildren.find(_.operator.id == op.id) // Find the root input, i.e., the one directly below a comparison rootInputNode <- inputNode.iterateParents.find(_.moveUp.exists(_.operator.isInstanceOf[Comparison])) // Get the comparison node comparisonNode <- rootInputNode.moveUp } { // The operator is a source input if it is the first child of the comparison isSource = comparisonNode.iterateChildren.next().operator.id == rootInputNode.operator.id } isSource } @connectOperators(op: Operator) = { } @connectOperator(op: Operator) = { @op match { case Aggregation(id, weight, aggregator, operators) => { @* Handle children *@ @for(op <- operators) { @connectOperator(op) } @* Create endpoints *@ var @targetEndpoint(id) = jsPlumb.addEndpoint('@GenerateId(id,true)', endpointSimilarityTarget); var @sourceEndpoint(id) = jsPlumb.addEndpoint('@GenerateId(id,true)', endpointSimilaritySource); @* Connect children *@ @for(op <- operators) { jsPlumb.connect({ source: @sourceEndpoint(op.id), target: @targetEndpoint(id) }); } } case Comparison(id, weight, threshold, indexing, metric, inputs) => { @* Handle children *@ @connectOperator(inputs.source) @connectOperator(inputs.target) @* Create endpoints *@ var @targetEndpoint(id) = jsPlumb.addEndpoint('@GenerateId(id,true)', endpointValueTarget); var @sourceEndpoint(id) = jsPlumb.addEndpoint('@GenerateId(id,true)', endpointSimilaritySource); @* Connect children *@ jsPlumb.connect({ source: @sourceEndpoint(inputs.source.id), target: @targetEndpoint(id) }); jsPlumb.connect({ source: @sourceEndpoint(inputs.target.id), target: @targetEndpoint(id) }); } case TransformInput(id, transformer, inputs) => { @* Handle children *@ @for(input <- inputs) { @connectOperator(input) } var @targetEndpoint(id) = jsPlumb.addEndpoint('@GenerateId(id,true)', endpointValueTarget); var @sourceEndpoint(id) = jsPlumb.addEndpoint('@GenerateId(id,true)', endpointValueSource); @* Connect children *@ @for(input <- inputs) { jsPlumb.connect({ source: @sourceEndpoint(input.id), target: @targetEndpoint(id) }); } } case PathInput(id, path) => { var @sourceEndpoint(id) = jsPlumb.addEndpoint('@GenerateId(id,true)', endpointValueSource); } } } @sourceEndpoint(id: String) = @{ "endpoint_" + id.replace('-', '$') + "_source" } @targetEndpoint(id: String) = @{ "endpoint_" + id.replace('-', '$') + "_target" }