File

packages/ast-to-mmd/src/app/graph-blocks/graph-block.ts

Description

Base class representing each node in graph.

Index

Properties
Methods
Accessors

Constructor

Protected constructor(id: string)

Construct new node in graph.

Parameters :
Name Type Optional Description
id string No

Unique ID in the node graph.

Properties

Private _lazyDependencies
Type : string[]
Default value : []

Lazy dependencies ready to render when this node is rendered.

Private _parent
Type : GraphBlock | undefined
Default value : undefined

Parent of current node in graph. Can be undefined.

Private _parentSiblingCondition
Type : SiblingCondition

SiblingCondition for finding correct parent.

Private _siblingCondition
Type : SiblingCondition
Private Static Readonly EMPTY_LINE_RENDERER_MODIFIER
Type : LineRendererModifier
Default value : () => {...}

Empty implementation of LineRendererModifier.

Public Readonly id
Type : string
Unique ID in the node graph.
Private Static Readonly POSITIVE_SIBLING_CONDITION
Type : SiblingCondition
Default value : new PositiveSiblingCondition()

Positive sibling condition default for all GraphBlock.

Private Static Readonly SHAPE_RENDERER
Type : ShapeRenderer
Default value : new ShapeRenderer()

Shape renderer for generating different shapes in graph.

Methods

Private __multiFindChildIndex
__multiFindChildIndex(children: GraphBlock[][], child: GraphBlock)

Private method for finding a child index of defined block in a multidimensional array of children.

Parameters :
Name Type Optional Description
children GraphBlock[][] No

[] Multidimensional array of children including the child.

child GraphBlock No

GraphBlock Wanted child.

Returns : | undefined

Coordinates of child block or undefined when block is not found.

Private __renderLines
__renderLines(indent: number, id: string, ids: string[], direction: "left" | "right", builderModifier: LineRendererModifier[])

Private method for rendering lines between nodes in defined directions.

Parameters :
Name Type Optional Description
indent number No

Indentation from start of the line.

id string No

ID of one side of connection.

ids string[] No

Array of IDs of end of a connection.

direction "left" | "right" No

Direction of a line.

builderModifier LineRendererModifier[] No

Optional modifier of current line.

Returns : string

Rendered line.

Protected _assignParent
_assignParent(children: GraphBlock[])

Helper method for assigning this parent into each children.

Parameters :
Name Type Optional Description
children GraphBlock[] No

[]GraphBlock Children where this parent is going to be assigned.

Returns : void
Protected _filterChildren
_filterChildren(children: GraphBlock[])

Helper method for filtering array of children.

Parameters :
Name Type Optional Description
children GraphBlock[] No

[]GraphBlock Array of children.

Returns : GraphBlock[]
Protected _findParentBySiblingCondition
_findParentBySiblingCondition(child: GraphBlock, siblingCondition: SiblingCondition)

Helper method for finding an indirect parent of defined child by SiblingCondition.

Parameters :
Name Type Optional Description
child GraphBlock No

GraphBlock Child whose parent is wanted.

siblingCondition SiblingCondition No

SiblingCondition.

Protected _findSiblingChild
_findSiblingChild(child: GraphBlock, parentSiblingCondition: SiblingCondition, siblingCondition: SiblingCondition)

Helper method for finding a sibling of defined child in parents children.

Parameters :
Name Type Optional Default value Description
child GraphBlock No

GraphBlock Node which for sibling is looked for.

parentSiblingCondition SiblingCondition No this.siblingCondition

SiblingCondition.

siblingCondition SiblingCondition No this.siblingCondition

SiblingCondition.

Protected _generateSpace
_generateSpace(count: number)

Helper method for generating space.

Parameters :
Name Type Optional Description
count number No

Count of space.

Returns : string

Space of defined length.

Protected _multiFilterChildren
_multiFilterChildren(children: GraphBlock[][])

Helper method for filtering children in multidimensional array of children.

Parameters :
Name Type Optional Description
children GraphBlock[][] No

[] Array of children.

Returns : [][]
Protected _renderConnectionWithParent
_renderConnectionWithParent(indent: number, children: GraphBlock, lastIds: string[], priorityBuilderModifier: LineRendererModifier[])
Parameters :
Name Type Optional
indent number No
children GraphBlock No
lastIds string[] No
priorityBuilderModifier LineRendererModifier[] No
Returns : string
Protected _renderDependencies
_renderDependencies(indent: number, blocks: GraphBlock[])

Helper method for rendering dependencies with defined indent between blocks.

Parameters :
Name Type Optional Description
indent number No

Indentation from start of the line.

blocks GraphBlock[] No

[]GraphBlock Blocks to render dependencies between each node.

Returns : string

Dependencies of blocks.

Protected _renderLine
_renderLine(indent: number, lhsId: string, rhsId: string, builderModifier: LineRendererModifier[])

Helper method for generating line between two nodes.

Parameters :
Name Type Optional Description
indent number No

Indentation from start of the line.

lhsId string No

ID of start node.

rhsId string No

ID of end node.

builderModifier LineRendererModifier[] No
Returns : string

Line definition between two nodes.

Protected _renderLinesL2R
_renderLinesL2R(indent: number, toId: string, fromIds: string[], builderModifier: LineRendererModifier[])

Helper method for rendering line starting in one node and ending in multiple nodes.

Parameters :
Name Type Optional Description
indent number No

Indentation from start of the line.

toId string No

ID of start node.

fromIds string[] No

Array of end nodes.

builderModifier LineRendererModifier[] No

Optional modifier of current line.

Returns : string

Rendered line.

Protected _renderLinesR2L
_renderLinesR2L(indent: number, fromId: string, toIds: string[], builderModifier: LineRendererModifier[])

Helper method for rendering line starting in multiple nodes and ending in one node.

Parameters :
Name Type Optional Description
indent number No

Indentation from start of the line.

fromId string No

Array of start nodes.

toIds string[] No

ID of end node.

builderModifier LineRendererModifier[] No

Optional modifier of current line.

Returns : string

Rendered line.

Protected _renderShape
_renderShape(text: string, shape: Shape)

Helper method for rendering defined shape with text.

Parameters :
Name Type Optional Description
text string No

Text in shape.

shape Shape No

Shape Type of shape.

Returns : string

Rendered shape with text.

Protected createLineBuilder
createLineBuilder(lhsId: string, rhsId: string)

Create new instance of LineRenderer.

Parameters :
Name Type Optional Description
lhsId string No

ID of start node.

rhsId string No

ID of end node.

Returns : LineRenderer
Public Abstract render
render(_indent: number)

Renders current node.

Parameters :
Name Type Optional Description
_indent number No

Indentation from start of the line.

Returns : string
Protected renderInSubgraph
renderInSubgraph(_indent: number, id: string, text: string, bodyRenderer: (indent: number) => void, direction: "TB" | "TD" | "BT" | "LR" | "RL")

Renders current node wrapped in a subgraph.

Direction options:

  • TB - top to bottom (default)
  • TD - top-down / same as top to bottom
  • BT - bottom to top
  • LR - left to rignt
  • RL - right to left
Parameters :
Name Type Optional Default value Description
_indent number No

Indentation from start of the line.

id string No

ID of a subgraph.

text string No

Name of a subgraph.

bodyRenderer function No

Callback responsible for rendering real node content.

direction "TB" | "TD" | "BT" | "LR" | "RL" No 'TB'

Direction of a subgraph.

Returns : string

Graph wrapped in a subgraph.

Public renderLazyDependencies
renderLazyDependencies(_indent: number)

Renders lazy dependencies of current node.

Parameters :
Name Type Optional Description
_indent number No

Indentiation from start of the line.

Returns : string
Public toString
toString()
Returns : string

Accessors

blockKind
getblockKind()
Returns : BlockKind
firstBlock
getfirstBlock()
Returns : GraphBlock
lastBlocks
getlastBlocks()
Returns : string[]
hasChildren
gethasChildren()
Returns : boolean
children
getchildren()
Returns : [][]
lazyDependency
setlazyDependency(lazyLine: string)

Add lazy dependency to local collection.

Parameters :
Name Type Optional Description
lazyLine string No

Rendered lazy dependency line.

Returns : void
includeInDependencyGraph
getincludeInDependencyGraph()
Returns : boolean
allowUnwrapChildren
getallowUnwrapChildren()
Returns : boolean
skipRenderRestDependencies
getskipRenderRestDependencies()
Returns : boolean
siblingCondition
getsiblingCondition()
Returns : SiblingCondition
setsiblingCondition(condition: SiblingCondition)
Parameters :
Name Type Optional Description
condition SiblingCondition No

SiblingCondition.

Returns : void
parentSiblingCondition
getparentSiblingCondition()
Returns : SiblingCondition
setparentSiblingCondition(condition: SiblingCondition)
Parameters :
Name Type Optional Description
condition SiblingCondition No

SiblingCondition.

Returns : void
connectWithDirectParent
getconnectWithDirectParent()

Should be used only when:

Returns : boolean
connectWithParentOfKind
getconnectWithParentOfKind()

Should be used only when:

Returns : boolean
connectWithNextParentsSibling
getconnectWithNextParentsSibling()

Should be used only when:

Returns : boolean
isDependencyBridge
getisDependencyBridge()

Should be used only when:

Returns : boolean
lineRendererModifier
getlineRendererModifier()

Should be used only when:

import { BlockKind } from '../block.kind';
import { PositiveSiblingCondition, SiblingCondition } from '../conditions';
import { LineRenderer, LineRendererModifier, Shape, ShapeRenderer } from './renderer';

/**
 * Base class representing each node in graph.
 */
export abstract class GraphBlock {
  /**
   * Shape renderer for generating different shapes in graph.
   */
  private static readonly SHAPE_RENDERER: ShapeRenderer = new ShapeRenderer();
  /**
   * Positive sibling condition default for all {@link GraphBlock}.
   */
  private static readonly POSITIVE_SIBLING_CONDITION: SiblingCondition = new PositiveSiblingCondition();
  /**
   * Empty implementation of {@link LineRendererModifier}.
   */
  private static readonly EMPTY_LINE_RENDERER_MODIFIER: LineRendererModifier = (lineRenderer: LineRenderer) => lineRenderer;

  /**
   * Parent of current node in graph. Can be undefined.
   */
  private _parent: GraphBlock | undefined = undefined;

  /**
   * Lazy dependencies ready to render when this node is rendered.
   */
  private _lazyDependencies: string[] = [];

  /**
   * {@link SiblingCondition}.
   */
  private _siblingCondition: SiblingCondition;
  /**
   * {@link SiblingCondition} for finding correct parent.
   */
  private _parentSiblingCondition: SiblingCondition;

  /**
   * Construct new node in graph.
   *
   * @param id {string} Unique ID in the node graph.
   */
  protected constructor(public readonly id: string) {
    this._siblingCondition = GraphBlock.POSITIVE_SIBLING_CONDITION;
    this._parentSiblingCondition = GraphBlock.POSITIVE_SIBLING_CONDITION;
  }

  /**
   * Renders current node.
   *
   * @param _indent {number} Indentation from start of the line.
   */
  public abstract render(_indent: number): string;

  /**
   * Renders lazy dependencies of current node.
   *
   * @param _indent {number} Indentiation from start of the line.
   */
  public renderLazyDependencies(_indent: number): string {
    return this._lazyDependencies
      .map((dependency: string) => `${this._generateSpace(_indent)}${dependency}`)
      .join('\n');
  }

  /**
   * Renders current node wrapped in a subgraph.
   *
   * Direction options:
   * - TB - top to bottom (default)
   * - TD - top-down / same as top to bottom
   * - BT - bottom to top
   * - LR - left to rignt
   * - RL - right to left
   *
   * @param _indent {number} Indentation from start of the line.
   * @param id {string} ID of a subgraph.
   * @param text {string} Name of a subgraph.
   * @param bodyRenderer {function} Callback responsible for rendering real node content.
   * @param direction {string} Direction of a subgraph.
   * @returns {string} Graph wrapped in a subgraph.
   */
  protected renderInSubgraph(
    _indent: number,
    id: string,
    text: string,
    bodyRenderer: (indent: number) => string,
    direction: 'TB' | 'TD' | 'BT' | 'LR' | 'RL' = 'TB'
  ): string {
    return `
${this._generateSpace(_indent)}subgraph ${id} ${this._renderShape(text, Shape.SHARP_EDGES)}
${this._generateSpace(_indent + 1)}direction ${direction}
${bodyRenderer(_indent + 1)}
${this._generateSpace(_indent)}end
`;
  }

  /**
   * @returns {@link BlockKind} of the block.
   */
  public abstract get blockKind(): BlockKind;

  /**
   * Returns parent of this node or undefined if node has no parent.
   *
   * @returns {{@link GraphBlock} | undefined}
   */
  get parent(): GraphBlock | undefined {
    return this._parent;
  }

  /**
   * Set parent of this node.
   * Can not set {undefined} value.
   *
   * @param value {@link GraphBlock} Parent of this node.
   */
  set parent(value: GraphBlock | undefined) {
    if (value) {
      this._parent = value;
    }
  }

  /**
   * @returns {string} First ID of this node.
   */
  public get firstBlock(): GraphBlock {
    return this;
  }

  /**
   * @returns {string[]} Array of last ID of nodes directly connected to this node.
   */
  public get lastBlocks(): string[] {
    return [this.id];
  }

  /**
   * @returns {boolean} True when this node has some children, False otherwise.
   */
  public get hasChildren(): boolean {
    return false;
  }

  /**
   * @returns {GraphBlock[][]} Multidimensional array of direct children of this node.
   */
  public get children(): GraphBlock[][] {
    return [];
  }

  /**
   * Add lazy dependency to local collection.
   *
   * @param lazyLine {string} Rendered lazy dependency line.
   */
  public set lazyDependency(lazyLine: string) {
    this._lazyDependencies.push(lazyLine);
  }

  /**
   * @returns {boolean} True, when this node should be included in dependency graph, False otherwise.
   */
  public get includeInDependencyGraph(): boolean {
    return true;
  }

  /**
   * @returns True, when this children of this node is allowed to unwrap, False otherwise.
   */
  public get allowUnwrapChildren(): boolean {
    return true;
  }

  /**
   * @returns True, when rest of the dependencies in a chain should not be rendered, False otherwise.
   */
  public get skipRenderRestDependencies(): boolean {
    return false;
  }

  /**
   * @returns {@link SiblingCondition}.
   */
  public get siblingCondition(): SiblingCondition {
    return this._siblingCondition;
  }

  /**
   * Set {@link SiblingCondition}.
   *
   * @param condition {@link SiblingCondition}.
   */
  protected set siblingCondition(condition: SiblingCondition) {
    this._siblingCondition = condition;
  }

  /**
   * @returns {@link SiblingCondition}.
   */
  public get parentSiblingCondition(): SiblingCondition {
    return this._parentSiblingCondition;
  }

  /**
   * Set {@link SiblingCondition}.
   *
   * @param condition {@link SiblingCondition}.
   */
  protected set parentSiblingCondition(condition: SiblingCondition) {
    this._parentSiblingCondition = condition;
  }

  /**
   * Should be used only when:
   * - {@link skipRenderRestDependencies} = true
   *
   * @returns True, when this block must be connected with its direct parent, False otherwise.
   */
  public get connectWithDirectParent(): boolean {
    return false;
  }

  /**
   * Should be used only when:
   * - {@link skipRenderRestDependencies} = true
   *
   * @returns True, when this block must be connected with parent of defined kind, False otherwise.
   */
  public get connectWithParentOfKind(): boolean {
    return false;
  }

  /**
   * Should be used only when:
   * - {@link skipRenderRestDependencies} = true
   *
   * @returns True, when this block must connect with next parents sibling, False otherwise.
   */
  public get connectWithNextParentsSibling(): boolean {
    return false;
  }

  /**
   * Should be used only when:
   * - {@link connectWithDirectParent} = true OR
   * - {@link connectWithParentOfKind} = true OR
   * - {@link connectWithNextParentsSibling} = true
   * <br>
   * In this case line configuration will be overriden by one from bridge block.
   *
   * @returns True, when this block serves only as a bridge between two blocks.
   */
  public get isDependencyBridge(): boolean {
    return false
  }

  /**
   * Should be used only when:
   * - {@link isDependencyBridge} = true
   *
   * @returns {@link LineRendererModifier} Modifier of current line renderer.
   */
  public get lineRendererModifier(): LineRendererModifier {
    return GraphBlock.EMPTY_LINE_RENDERER_MODIFIER;
  }

  /**
   * Create new instance of {@link LineRenderer}.
   *
   * @param lhsId {string} ID of start node.
   * @param rhsId {string} ID of end node.
   * @returns {@link LineRenderer} New instance of {@link LineRenderer}
   */
  protected createLineBuilder(lhsId: string, rhsId: string): LineRenderer {
    return new LineRenderer(lhsId, rhsId);
  }

  // ----------------------------- HELPER METHODS ------------------------------

  /**
   * Helper method for assigning this parent into each children.
   *
   * @param children {@link GraphBlock[]} Children where this parent is going to be assigned.
   */
  protected _assignParent(children: GraphBlock[]): void {
    children.forEach((child: GraphBlock) => {
      child.parent = this;
    });
  }

  /**
   * Helper method for generating space.
   *
   * @param count {number} Count of space.
   * @returns {string} Space of defined length.
   */
  protected _generateSpace(count: number): string {
    return ' '.repeat(count);
  }

  /**
   * Helper method for generating line between two nodes.
   *
   * @param indent {number} Indentation from start of the line.
   * @param lhsId {string} ID of start node.
   * @param rhsId {string} ID of end node.
   * @param builderModifier
   * @returns {string} Line definition between two nodes.
   */
  protected _renderLine(
    indent: number,
    lhsId: string,
    rhsId: string,
    ...builderModifier: LineRendererModifier[]
  ): string {
    let builder = this.createLineBuilder(lhsId, rhsId);
    if (builderModifier) {
      builder = builderModifier.reduce((prev: LineRenderer, curr: LineRendererModifier) => curr(builder), builder);
    }

    return `${this._generateSpace(indent + 1)}${builder.render()}`;
  }

  /**
   * Helper method for rendering defined shape with text.
   *
   * @param text {string} Text in shape.
   * @param shape {@link Shape} Type of shape.
   * @returns {string} Rendered shape with text.
   */
  protected _renderShape(text: string, shape: Shape): string {
    return GraphBlock.SHAPE_RENDERER.render(text, shape);
  }

  /**
   * Helper method for rendering dependencies with defined indent between blocks.
   *
   * @param indent {number} Indentation from start of the line.
   * @param blocks {@link GraphBlock[]} Blocks to render dependencies between each node.
   * @returns {string} Dependencies of blocks.
   */
  protected _renderDependencies(indent: number, blocks: GraphBlock[]): string {
    blocks = blocks.filter((block: GraphBlock) => block.includeInDependencyGraph);
    if (blocks.length <= 1) {
      return '';
    }

    let result = '';
    let lastBlockWithDependency = blocks[0];

    for (let i = 1; i < blocks.length; i++) {
      const currentBlockWithDependency: GraphBlock = blocks[i];
      const currentFirstBlock: GraphBlock = currentBlockWithDependency.firstBlock;
      const lastIds: string[] = lastBlockWithDependency.lastBlocks;

      if (!currentBlockWithDependency.isDependencyBridge) {
        result += this._renderLinesL2R(indent, currentFirstBlock.id, lastIds);
      }
      if (currentBlockWithDependency.skipRenderRestDependencies) {
        result += this._renderConnectionWithParent(indent, currentBlockWithDependency, lastIds);
        break;
      }

      lastBlockWithDependency = currentBlockWithDependency;
    }

    return result;
  }

  /**
   * Helper method for rendering line starting in one node and ending in multiple nodes.
   *
   * @param indent {number} Indentation from start of the line.
   * @param toId {string} ID of start node.
   * @param fromIds {string[]} Array of end nodes.
   * @param builderModifier {function} Optional modifier of current line.
   * @returns {string} Rendered line.
   */
  protected _renderLinesL2R(
    indent: number,
    toId: string,
    fromIds: string[],
    ...builderModifier: LineRendererModifier[]
  ): string {
    return this.__renderLines(indent, toId, fromIds, 'right', ...builderModifier);
  }

  /**
   * Helper method for rendering line starting in multiple nodes and ending in one node.
   *
   * @param indent {number} Indentation from start of the line.
   * @param fromId {string} Array of start nodes.
   * @param toIds {string[]} ID of end node.
   * @param builderModifier {function} Optional modifier of current line.
   * @returns {string} Rendered line.
   */
  protected _renderLinesR2L(
    indent: number,
    fromId: string,
    toIds: string[],
    ...builderModifier: LineRendererModifier[]
  ): string {
    return this.__renderLines(indent, fromId, toIds, 'left', ...builderModifier);
  }

  protected _renderConnectionWithParent(indent: number, children: GraphBlock, lastIds: string[], ...priorityBuilderModifier: LineRendererModifier[]): string {
    if (!children.parent) {
      return '';
    }

    let result = '';

    if (children.connectWithDirectParent) {
      if (children.isDependencyBridge) {
        result += this._renderLinesL2R(indent, children.parent.id, lastIds, ...priorityBuilderModifier, children.lineRendererModifier);
      } else {
        result += this._renderLine(indent, children.id, children.parent.id);
      }
    }

    if (children.connectWithParentOfKind) {
      const parentOfKind = this._findParentBySiblingCondition(children, children.siblingCondition);
      if (parentOfKind) {
        if (children.isDependencyBridge) {
          result += this._renderLinesL2R(indent, parentOfKind.id, lastIds, ...priorityBuilderModifier, children.lineRendererModifier);
        } else {
          result += this._renderLine(indent, children.id, parentOfKind.id);
        }
      }
    }

    if (children.connectWithNextParentsSibling) {
      const siblingChild: GraphBlock | undefined = this._findSiblingChild(this, children.parentSiblingCondition, children.siblingCondition);
      if (siblingChild) {
        if (children.isDependencyBridge) {
          siblingChild.lazyDependency = this._renderLinesL2R(0, siblingChild.id, lastIds, ...priorityBuilderModifier, children.lineRendererModifier);
        } else {
          result += this._renderLine(indent, children.id, siblingChild.id);
        }
      }
    }

    return result;
  }

  /**
   * Helper method for filtering children in multidimensional array of children.
   *
   * @param children {@link GraphBlock[][]} Array of children.
   * @returns {@link GraphBlock[][]} Multidimensional array of children ready for rendering.
   */
  protected _multiFilterChildren(children: GraphBlock[][]): GraphBlock[][] {
    return children.filter((childs: GraphBlock[]) => this._filterChildren(childs));
  }

  /**
   * Helper method for filtering array of children.
   *
   * @param children {@link GraphBlock[]} Array of children.
   * @returns {@link GraphBlock[]} Children ready for rendering.
   */
  protected _filterChildren(children: GraphBlock[]): GraphBlock[] {
    return children.filter((block: GraphBlock) => block.includeInDependencyGraph);
  }

  /**
   * Helper method for finding a sibling of defined child in parents children.
   *
   * @param child {@link GraphBlock} Node which for sibling is looked for.
   * @param parentSiblingCondition {@link SiblingCondition}.
   * @param siblingCondition {@link SiblingCondition}.
   * @returns {@link GraphBlock} | {@link undefined} Sibling of wanted child or undefined when sibling was not possible to find.
   */
  protected _findSiblingChild(child: GraphBlock, parentSiblingCondition: SiblingCondition = this.siblingCondition, siblingCondition: SiblingCondition = this.siblingCondition): GraphBlock | undefined {
    if (!child.parent) {
      return undefined;
    }

    const parent = child.parent;
    if (!parentSiblingCondition.isValid(child)) {
      return this._findSiblingChild(child.parent, parentSiblingCondition, siblingCondition);
    }

    const filteredParentChildren = this._multiFilterChildren(parent.children);
    const result: [number, number] | undefined = this.__multiFindChildIndex(filteredParentChildren, child);
    if (result) {
      const [outerIndexOfThisBlock, innerIndexOfThisBlock]: [number, number] = result;
      const childrenCount = filteredParentChildren[outerIndexOfThisBlock].length;
      if (childrenCount - 1 > innerIndexOfThisBlock) {
        for (let i = innerIndexOfThisBlock + 1; i < childrenCount; i++) {
          const sibling = filteredParentChildren[outerIndexOfThisBlock][i];
          if (siblingCondition.isValid(sibling)) {
            return sibling;
          }
        }
        // const sibling = filteredParentChildren[outerIndexOfThisBlock][innerIndexOfThisBlock + 1];
        // if (siblingCondition.isValid(sibling)) {
        //   return sibling;
        // } else {
          return this._findSiblingChild(parent, parentSiblingCondition, siblingCondition);
        // }
      } else {
        return this._findSiblingChild(parent, parentSiblingCondition, siblingCondition);
      }
    } else {
      return undefined;
    }
  }

  /**
   * Helper method for finding an indirect parent of defined child by {@link SiblingCondition}.
   *
   * @param child {@link GraphBlock} Child whose parent is wanted.
   * @param siblingCondition {@link SiblingCondition}.
   * @returns {@link GraphBlock} | {@link undefined} Parent of wanted child or undefined when parent was not possible to find.
   */
  protected _findParentBySiblingCondition(child: GraphBlock, siblingCondition: SiblingCondition): GraphBlock | undefined {
    if (!child.parent) {
      return undefined;
    }

    const parent = child.parent;

    if (siblingCondition.isValid(parent)) {
      return parent;
    }

    return this._findParentBySiblingCondition(parent, siblingCondition);
  }
  // ----------------------------- PRIVATE METHODS -----------------------------

  /**
   * Private method for rendering lines between nodes in defined directions.
   *
   * @param indent {number} Indentation from start of the line.
   * @param id {string} ID of one side of connection.
   * @param ids {string[]} Array of IDs of end of a connection.
   * @param direction {string} Direction of a line.
   * @param builderModifier {function} Optional modifier of current line.
   * @returns {string} Rendered line.
   */
  private __renderLines(
    indent: number,
    id: string,
    ids: string[],
    direction: 'left' | 'right',
    ...builderModifier: LineRendererModifier[]
  ): string {
    let result = ids
      .map((currentId) => {
        if (direction === 'left') {
          return this._renderLine(indent + 1, id, currentId, ...builderModifier);
        } else {
          return this._renderLine(indent + 1, currentId, id, ...builderModifier);
        }
      })
      .join('\n');
    result += '\n';

    return result;
  }

  // noinspection JSMethodCanBeStatic
  /**
   * Private method for finding a child index of defined block in a multidimensional array of children.
   *
   * @param children {@link GraphBlock[][]} Multidimensional array of children including the child.
   * @param child {@link GraphBlock} Wanted child.
   * @returns {[number, number] | undefined} Coordinates of child block or undefined when block is not found.
   */
  private __multiFindChildIndex(children: GraphBlock[][], child: GraphBlock): [number, number] | undefined {
    for (let i = 0; i < children.length; i++) {
      const parallelChildren = children[i];
      for (let j = 0; j < parallelChildren.length; j++) {
        const innerChildren = parallelChildren[j];
        if (innerChildren === child) {
          return [i, j];
        }
      }
    }

    return undefined;
  }

  public toString(): string {
    return this.id;
  }
}

results matching ""

    No results matching ""