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

scalaz.xml.cursor.Cursor.scala Maven / Gradle / Ivy

There is a newer version: 7.1.17
Show newest version
package scalaz
package xml
package cursor

sealed trait Cursor {
  import Cursor._

  val current: Content
  val lefts: List[Content]
  val rights: List[Content]
  val parents: Path

  import QName._

  def unary_- : Content =
    root.current

  // alias for current
  def unary_~ : Content =
    current

  /// setContent
  def setCurrent(c: Content): Cursor =
    cursor(c, lefts, rights, parents)

  /// setContent (alias)
  def !(c: Content): Cursor =
    setCurrent(c)

  /// modifyContent
  def withCurrent(c: Content => Content): Cursor =
    cursor(c(current), lefts, rights, parents)

  /// alias for withCurrent
  def -->>(c: Content => Content): Cursor =
    withCurrent(c)

  def setLefts(l: List[Content]): Cursor =
    cursor(current, l, rights, parents)

  def withLefts(l: List[Content] => List[Content]): Cursor =
    cursor(current, l(lefts), rights, parents)

  def setRights(l: List[Content]): Cursor =
    cursor(current, l, rights, parents)

  def withRights(l: List[Content] => List[Content]): Cursor =
    cursor(current, lefts, l(rights), parents)

  /// insertLeft
  def <<<:(c: Content): Cursor =
    withLefts(c :: _)

  /// insertRight
  def :>>>(c: Content): Cursor =
    withRights(c :: _)

  /// parent
  def parent: Option[Cursor] =
    unconsOption(parents) {
      case ((pls, v, prs), ps) =>
        cursor(
            current = Content.elem(v fromTag (combChildren(lefts, current, rights)))
          , lefts = pls
          , rights = prs
          , parents = ps
        )
    }

  // alias for parent
  def ^ : Option[Cursor] =
    parent

  def parentOr(c: => Cursor): Cursor =
    parent getOrElse c

  /// root
  def root: Cursor = {
    @annotation.tailrec
    def rooot(c: Cursor): Cursor =
      c.parent match {
        case None => c
        case Some(r) => rooot(r)
      }

    rooot(this)
  }

  /// isRoot
  def isRoot: Boolean =
    parents.isEmpty

  /// isFirst
  def isFirst: Boolean =
    lefts.isEmpty

  /// isLast
  def isLast: Boolean =
    rights.isEmpty

  /// isLeaf
  def isLeaf: Boolean =
    current.fold(
      _ => false
    , _ => true
    , _ => true
    , _ => false
    )

  /// getNodeIndex
  def nodeIndex: Int =
    lefts.length

  /// hasChildren
  def hasChildren: Boolean =
    !isLeaf

  /// isChild
  def isChild: Boolean =
    !isRoot

  /// left
  def left: Option[Cursor] =
    unconsOption(lefts)((t: Content, ts: List[Content]) => cursor(t, ts, current :: rights, parents))

  def leftOr(c: => Cursor): Cursor =
    left getOrElse c

  /// right
  def right: Option[Cursor] =
    unconsOption(rights)((t: Content, ts: List[Content]) => cursor(t, current :: lefts, ts, parents))

  def rightOr(c: => Cursor): Cursor =
    right getOrElse c

  /// firstChild
  def firstChild: Option[Cursor] =
    downParents flatMap {
      case (ts, ps) =>
        unconsOption(ts)((h: Content, t: List[Content]) => cursor(
          current = h,
          lefts = Nil,
          rights = t,
          parents = ps
        ))
    }

  def firstChildOr(c: => Cursor): Cursor =
    firstChild getOrElse c

  /// lastChild
  def lastChild: Option[Cursor] =
    downParents flatMap {
      case (ts, ps) =>
        unconsOption(ts.reverse)((h: Content, t: List[Content]) => cursor(
          current = h,
          lefts = t,
          rights = Nil,
          parents = ps
        ))
    }

  def lastChildOr(c: => Cursor): Cursor =
    lastChild getOrElse c


  /// findLeft
  def findLeft(p: Cursor => Boolean): Option[Cursor] =
    left flatMap (l => if (p(l)) Some(l) else l.findLeft(p))

  def findLeftOr(p: Cursor => Boolean, c: => Cursor): Cursor =
    findLeft(p) getOrElse c

  /// findRight
  def findRight(p: Cursor => Boolean): Option[Cursor] =
    right flatMap (r => if (p(r)) Some(r) else r.findRight(p))

  def findRightOr(p: Cursor => Boolean, c: => Cursor): Cursor =
    findRight(p) getOrElse c

  /// findChild
  def findChild(p: Cursor => Boolean): Option[Cursor] =
    firstChild flatMap (f => if (p(f)) Some(f) else f.findRight(p))

  def findChildOr(p: Cursor => Boolean, c: => Cursor): Cursor =
    findChild(p) getOrElse c

  def findChildElementQname(p: QName => Boolean): Option[Cursor] =
    findChild(c => c.current.elem exists (e => p(e.name)))

  def findChildElementQnameOr(p: QName => Boolean, c: => Cursor): Cursor =
    findChildElementQname(p) getOrElse c

  def findChildElementName(p: String => Boolean): Option[Cursor] =
    findChildElementQname(n => p(n.name.mkString))

  def findChildElementNameOr(p: String => Boolean, c: => Cursor): Cursor =
    findChildElementName(p) getOrElse c

  /// nextDF
  def nextDepthFirst: Option[Cursor] = {
    def up(x: Cursor): Option[Cursor] =
      x.right orElse (x.parent flatMap (up(_)))

    firstChild orElse up(this)
  }

  def nextDepthFirstOr(c: => Cursor): Cursor =
    nextDepthFirst getOrElse c

  /// findRec
  def findRec(p: Cursor => Boolean): Option[Cursor] =
    if(p(this))
      Some(this)
    else
      nextDepthFirst flatMap (_ findRec p)

  def findRecOr(p: Cursor => Boolean, c: => Cursor): Cursor =
    findRec(p) getOrElse c

  /// getChild
  def nthChild(n: => Int): Option[Cursor] =
    for {
      (ts, ps) <- downParents
      (ls, t, rs) <- splitChildren(ts, n)
    } yield cursor(
      current = t,
      lefts = ls,
      rights = rs,
      parents = ps
    )

  def getChildOr(n: => Int, c: => Cursor): Cursor =
    nthChild(n) getOrElse c

  /// toTree
  def toTree: Content =
    root.current

  /// toForest
  def toForest: List[Content] = {
    val r = root
    combChildren(r.lefts, r.current, r.rights)
  }

  // The new position is:
  // * right if exists, or
  // * left if exists, or
  // * parent
  def remove: Option[(Content, Cursor)] =
    rights match {
      case h::t => Some((current, setCurrent(h).setRights(t)))
      case Nil =>
        lefts match {
          case h::t => Some((current, setCurrent(h).setLefts(t)))
          case Nil => parent map (p => (current, removeRights.removeLefts))
        }
    }

  def removeOr(c: => (Content, Cursor)) =
    remove getOrElse c

  def iremove: Option[Cursor] =
    remove map (_._2)

  def iremoveOr(c: => Cursor): Cursor =
    iremove getOrElse c

  def removeLefts: Cursor =
    setLefts(Nil)

  def removeRights: Cursor =
    setRights(Nil)

  /// removeLeft
  def removeLeft: Option[(Content, Cursor)] =
    unconsOption(lefts) {
      case (l, ls) => (l, setLefts(ls))
    }

  def removeLeftOr(c: => (Content, Cursor)): (Content, Cursor) =
    removeLeft getOrElse c

  /// removeRight
  def removeRight: Option[(Content, Cursor)] =
    unconsOption(rights) {
      case (r, rs) => (r, setRights(rs))
    }

  def removeRightOr(c: => (Content, Cursor)): (Content, Cursor) =
    removeRight getOrElse c

  /// insertGoLeft
  def insertGoLeft(t: Content): Cursor =
    setCurrent(t) :>>> current

  /// insertGoRight
  def insertGoRight(t: Content): Cursor =
    current <<<: setCurrent(t)

  /// removeGoLeft
  def removeGoLeft(t: Content): Option[Cursor] =
    unconsOption(lefts) {
      case (h, t) => setCurrent(h).setLefts(t)
    }

  def removeGoLeftOr(t: Content, c: => Cursor) =
    removeGoLeft(t) getOrElse(c)

  // removeGoRight
  def removeGoRight(t: Content): Option[Cursor] =
    unconsOption(rights) {
      case (h, t) => setCurrent(h).setRights(t)
    }

  def removeGoRightOr(t: Content, c: => Cursor): Cursor =
    removeGoRight(t) getOrElse c

  /// removeGoUp
  def removeGoUp: Option[Cursor] =
    unconsOption(parents) {
      case ((pls, v, prs), ps) =>
        cursor(
          current = Content.elem(v.fromTag(lefts reverse_::: rights))
        , lefts = pls
        , rights = prs
        , parents = ps
        )
    }

  def removeGoUpOr(c: => Cursor): Cursor =
    removeGoUp getOrElse c

  def elem: Option[Element] =
    current.elem

  def elemOr(e: => Element): Element =
    elem getOrElse e

  def isElem: Boolean =
    elem.isDefined

  def text: Option[CData] =
    current.text

  def textOr(d: => CData): CData =
    text getOrElse d

  def isText: Boolean =
    text.isDefined

  def cref: Option[Str] =
    current.cref

  def crefOr(s: => Str): Str =
    cref getOrElse s

  def isCref: Boolean =
    cref.isDefined

  def comment: Option[Str] =
    current.comment

  def commentOr(s: => Str): Str =
    comment getOrElse s

  def isComment: Boolean =
    comment.isDefined

  def usingElem(k: Element => Element): Cursor =
    withCurrent(_ usingElem k)

  def usingText(k: CData => CData): Cursor =
    withCurrent(_ usingText k)

  def usingCref(k: Str => Str): Cursor =
    withCurrent(_ usingCref k)

  def usingComment(k: Str => Str): Cursor =
    withCurrent(_ usingComment k)

  @annotation.tailrec
  final def walk(k: Cursor => Content): Cursor = {
    def up(c: Cursor): Option[Cursor] =
      c.parent flatMap(p => p.right orElse up(p))

    val x = setCurrent(k(this))
    x.firstChild orElse x.right orElse up(x) match {
      case None => x
      case Some(c) => c walk k
    }
  }

  /// splitChildren
  private def splitChildren[A](c: List[A], n: Int): Option[(List[A], A, List[A])] =
    if(n < 0)
      None
    else {
      @annotation.tailrec
      def loop(acc: List[A], z: List[A], w: Int): Option[(List[A], A, List[A])] =
        z match {
          case Nil =>
            None
          case x::xs =>
            if(w == 0)
              Some(acc, x, xs)
            else
              loop(x::acc, xs, n - 1)
        }

      loop(Nil, c, n)
    }

  /// combChildren
  private def combChildren[A](ls: List[A], t: A, rs: List[A]): List[A] =
    ls.foldLeft(t :: rs)((a, b) => b :: a)

  /// downParents
  private def downParents: Option[(List[Content], Path)] =
    current.elem map (e => (e.content, (lefts, e.tag, rights) :: parents))

  private def unconsOption[A, B](a: List[A])(f: (A, List[A]) => B): Option[B] =
    a match {
      case Nil => None
      case h :: t => Some(f(h, t))
    }

}

trait Cursors {
  type Path =
  List[(List[Content], Tag, List[Content])]

  def cursor(current: Content, lefts: List[Content] = Nil, rights: List[Content] = Nil, parents: Path = Nil): Cursor = {
    val c = current
    val l = lefts
    val r = rights
    val p = parents
    new Cursor {
      val current = c
      val lefts = l
      val rights = r
      val parents = p
    }
  }

  import std.AllInstances._

  implicit val CursorShow: Show[Cursor] = new Show[Cursor] {
    override def show(c: Cursor) =
      ("Cursor{current=" + Show[Content].shows(c.current) + ",lefts=" + Show[List[Content]].shows(c.lefts) + ",rights=" + Show[List[Content]].shows(c.rights) + ",parents=" + Show[Path].shows(c.parents))
  }

  implicit val CursorEqual: Equal[Cursor] =
    Equal.equalBy(c => (c.current, c.lefts, c.rights, c.parents))

}

object Cursor extends Cursors {

  import Lens._
  import StoreT._

  val currentCursorL: Cursor @> Content =
    lens(x => store(x.current)(b => cursor(b, x.lefts, x.rights, x.parents)))

  val leftsCursorL: Cursor @> List[Content] =
    lens(x => store(x.lefts)(b => cursor(x.current, b, x.rights, x.parents)))

  val rightsCursorL: Cursor @> List[Content] =
    lens(x => store(x.rights)(b => cursor(x.current, x.lefts, b, x.parents)))

  val parentsCursorL: Cursor @> Path =
    lens(x => store(x.parents)(b => cursor(x.current, x.lefts, x.rights, b)))

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy