All Downloads are FREE. Search and download functionalities are using the official Maven repository.

pl.touk.nussknacker.engine.canonize.ProcessCanonizer.scala Maven / Gradle / Ivy

There is a newer version: 1.18.0
Show newest version
package pl.touk.nussknacker.engine.canonize

import cats.data.{NonEmptyList, ValidatedNel}
import cats.instances.list._
import cats.syntax.traverse._
import pl.touk.nussknacker.engine.canonicalgraph._
import pl.touk.nussknacker.engine.graph._
import pl.touk.nussknacker.engine.graph.node.{BranchEnd, BranchEndData}
import pl.touk.nussknacker.engine.util.Implicits.RichScalaMap

object ProcessCanonizer {

  import MaybeArtificial.applicative
  import cats.syntax.apply._

  def canonize(process: EspProcess): CanonicalProcess = {
    CanonicalProcess(
      process.metaData,
      NodeCanonizer.canonize(process.roots.head),
      process.roots.tail.map(NodeCanonizer.canonize)
    )
  }

  def uncanonize(canonicalProcess: CanonicalProcess): ValidatedNel[ProcessUncanonizationError, EspProcess] =
    uncanonizeArtificial(canonicalProcess).toValidNel

  def uncanonizeArtificial(canonicalProcess: CanonicalProcess): MaybeArtificial[EspProcess] = {

    val branches: MaybeArtificial[NonEmptyList[pl.touk.nussknacker.engine.graph.node.SourceNode]] =
      canonicalProcess.allStartNodes.map(uncanonizeSource).sequence

    branches.map(bList => EspProcess(canonicalProcess.metaData, bList))
  }

  private def uncanonizeSource(canonicalNode: List[canonicalnode.CanonicalNode]): MaybeArtificial[node.SourceNode] =
    canonicalNode match {
      case (a @ canonicalnode.FlatNode(data: node.StartingNodeData)) :: tail =>
        uncanonize(a, tail).map(node.SourceNode(data, _))

      case other :: _ =>
        MaybeArtificial.artificialSource(InvalidRootNode(other.id))

      case _ =>
        MaybeArtificial.artificialSource(EmptyProcess)
    }

  private def uncanonize(
      previous: canonicalnode.CanonicalNode,
      canonicalNode: List[canonicalnode.CanonicalNode]
  ): MaybeArtificial[node.SubsequentNode] =
    canonicalNode match {
      case canonicalnode.FlatNode(data: node.BranchEndData) :: Nil =>
        new MaybeArtificial(node.BranchEnd(data), Nil)

      case canonicalnode.FlatNode(data: node.EndingNodeData) :: Nil =>
        new MaybeArtificial(node.EndingNode(data), Nil)

      case (a @ canonicalnode.FlatNode(data: node.OneOutputSubsequentNodeData)) :: tail =>
        uncanonize(a, tail).map(node.OneOutputSubsequentNode(data, _))

      case (a @ canonicalnode.FilterNode(data, nextFalse)) :: tail if nextFalse.isEmpty =>
        uncanonize(a, tail).map(nextTrue => node.FilterNode(data, Some(nextTrue), None))

      case (a @ canonicalnode.FilterNode(data, nextFalse)) :: tail if tail.isEmpty =>
        uncanonize(a, nextFalse).map { nextFalseV =>
          node.FilterNode(data, None, Some(nextFalseV))
        }

      case (a @ canonicalnode.FilterNode(data, nextFalse)) :: tail =>
        (uncanonize(a, tail), uncanonize(a, nextFalse)).mapN { (nextTrue, nextFalseV) =>
          node.FilterNode(data, Some(nextTrue), Some(nextFalseV))
        }

      case (a @ canonicalnode.SwitchNode(data, Nil, defaultNext)) :: Nil =>
        uncanonize(a, defaultNext).map { defaultNextV =>
          node.SwitchNode(data, Nil, Some(defaultNextV))
        }

      case (a @ canonicalnode.SwitchNode(data, nexts, defaultNext)) :: Nil if defaultNext.isEmpty =>
        nexts
          .map { casee =>
            uncanonize(a, casee.nodes).map(node.Case(casee.expression, _))
          }
          .sequence[MaybeArtificial, node.Case]
          .map(node.SwitchNode(data, _, None))

      case (a @ canonicalnode.SwitchNode(data, nexts, defaultNext)) :: Nil =>
        val unFlattenNexts = nexts
          .map { casee =>
            uncanonize(a, casee.nodes).map(node.Case(casee.expression, _))
          }
          .sequence[MaybeArtificial, node.Case]

        (unFlattenNexts, uncanonize(a, defaultNext)).mapN { (nextsV, defaultNextV) =>
          node.SwitchNode(data, nextsV, Some(defaultNextV))
        }

      case (a @ canonicalnode.SplitNode(bare, Nil)) :: Nil =>
        MaybeArtificial.artificialSink(InvalidTailOfBranch(bare.id))

      case (a @ canonicalnode.SplitNode(bare, nexts)) :: Nil =>
        nexts.map(uncanonize(a, _)).sequence[MaybeArtificial, node.SubsequentNode].map { uncanonized =>
          node.SplitNode(bare, uncanonized)
        }

      case invalidHead :: _ =>
        MaybeArtificial.artificialSink(InvalidTailOfBranch(invalidHead.id))

      case Nil =>
        MaybeArtificial.artificialSink(InvalidTailOfBranch(previous.id))
    }

}

object NodeCanonizer {

  def canonize(n: node.Node): List[canonicalnode.CanonicalNode] =
    n match {
      case oneOut: node.OneOutputNode =>
        canonicalnode.FlatNode(oneOut.data) :: canonize(oneOut.next)
      case node.FilterNode(data, nextTrue, nextFalse) =>
        canonicalnode.FilterNode(data, nextFalse.toList.flatMap(canonize)) :: nextTrue.toList.flatMap(canonize)
      case node.SwitchNode(data, nexts, defaultNext) =>
        canonicalnode.SwitchNode(
          data = data,
          nexts = nexts.map { next =>
            canonicalnode.Case(next.expression, canonize(next.node))
          },
          defaultNext = defaultNext.toList.flatMap(canonize)
        ) :: Nil
      case ending: node.EndingNode =>
        canonicalnode.FlatNode(ending.data) :: Nil
      case node.SplitNode(bare, nexts) =>
        canonicalnode.SplitNode(bare, nexts.map(canonize)) :: Nil
      case node.FragmentNode(input, nexts) =>
        canonicalnode.Fragment(input, nexts.mapValuesNow(canonize)) :: Nil
      case BranchEnd(e: BranchEndData) =>
        canonicalnode.FlatNode(e) :: Nil
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy