From 94e1e1c0c6cd939035585651d77c563067536350 Mon Sep 17 00:00:00 2001 From: Tim Nieradzik Date: Mon, 11 May 2020 23:13:32 +0100 Subject: [PATCH] Node: Add textContent function Closes #65. --- src/main/scala/pine/Node.scala | 27 +++++++++++++++++++++------ src/test/scala/pine/NodeSpec.scala | 26 ++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/main/scala/pine/Node.scala b/src/main/scala/pine/Node.scala index 6a910b8..65edd09 100644 --- a/src/main/scala/pine/Node.scala +++ b/src/main/scala/pine/Node.scala @@ -19,14 +19,18 @@ sealed trait Node { def map(f: Node => Node): T def flatMap(f: Node => List[Node]): T def mapFirst(f: PartialFunction[Node, Node]): T + + /** Recursively traverses tree and returns content of first text node */ + def textContent: Option[String] } case class Text(text: String) extends Node { override type T = Text - def map(f: Node => Node): T = this - def flatMap(f: Node => List[Node]): T = this - def mapFirst(f: PartialFunction[Node, Node]): T = this + override def map(f: Node => Node): T = this + override def flatMap(f: Node => List[Node]): T = this + override def mapFirst(f: PartialFunction[Node, Node]): T = this + override def textContent: Option[String] = Some(text) } case class Tag[TagName <: Singleton](tagName : String with TagName, @@ -129,7 +133,7 @@ case class Tag[TagName <: Singleton](tagName : String with TagName, } /** Recursively map children, excluding root node */ - def map(f: Node => Node): Tag[TagName] = set(children.map(f(_).map(f))) + override def map(f: Node => Node): Tag[TagName] = set(children.map(f(_).map(f))) /** Recursively map tag children, including root node */ def mapRoot(f: Tag[_] => Tag[_]): Tag[TagName] = { @@ -142,10 +146,10 @@ case class Tag[TagName <: Singleton](tagName : String with TagName, iter(this).asInstanceOf[T] } - def flatMap(f: Node => List[Node]): Tag[TagName] = + override def flatMap(f: Node => List[Node]): Tag[TagName] = copy(children = children.flatMap(n => f(n.flatMap(f)))) - def mapFirst(f: PartialFunction[Node, Node]): Tag[TagName] = { + override def mapFirst(f: PartialFunction[Node, Node]): Tag[TagName] = { var done = false def m(n: Node): Node = @@ -168,6 +172,17 @@ case class Tag[TagName <: Singleton](tagName : String with TagName, def partialMap(f: PartialFunction[Node, Node]): Tag[TagName] = map(node => f.lift(node).getOrElse(node)) + override def textContent: Option[String] = { + for (c <- children) { + c.textContent match { + case Some(c) => return Some(c) + case _ => + } + } + + None + } + /** * Recursively adds `suffix` to every given attribute. * diff --git a/src/test/scala/pine/NodeSpec.scala b/src/test/scala/pine/NodeSpec.scala index f928b23..7712bc5 100644 --- a/src/test/scala/pine/NodeSpec.scala +++ b/src/test/scala/pine/NodeSpec.scala @@ -406,4 +406,30 @@ class NodeSpec extends FunSuite { assert(tag.Div.dataLanguage(Language.Spanish).toHtml == """
""") } + + test("textContent") { + // top-level text node + val n = Text("hello world") + assert(n.textContent == Some("hello world")) + + // an empty string is treated like a regular text node + val n2 = Text("") + assert(n2.textContent == Some("")) + + // one nesting level + val n3 = tag.Div.set(Text("hello")) + assert(n3.textContent == Some("hello")) + + // two nesting levels + val n4 = tag.Div.set(tag.Div.set(Text("hello"))) + assert(n4.textContent == Some("hello")) + + // return only first text node + val n5 = tag.Div.set(List(tag.Div.set(Text("hello")), Text(" world"))) + assert(n5.textContent == Some("hello")) + + // no text node found + val n6 = tag.Div + assert(n6.textContent == None) + } }