Permalink
Browse files

Make PathCreator produce lazy Traversables rather than realized Seqs

  • Loading branch information...
1 parent 495b340 commit febd26a308d02b024de6078271e20b22d3d27fa8 @josharnold52 committed Oct 19, 2011
@@ -34,157 +34,137 @@ import util.VectorCase
/** Defines type related to paths on a tree.
* Also contains factory methods for [[PathFunction]]s */
private[antixml] object PathCreator {
-
- /** The number represents the number of the node in its parent's children list.
- * In case the node is root, the number is its position in the group to which it belongs.
- */
- private[antixml] type Location = Int
/** The basic result type of a match.
* @param value the selector result
* @param path the top-down path to the selected node.
*/
- private[antixml] case class PathVal[+A](value: A, path: IndexedSeq[Int])
-
- /** The values from a path function in raw form. */
- type PathVals[+A] = Seq[(PathVal[A])]
-
- /** A function that creates paths on group, to be used when constructing zippers. */
- type PathFunction[+A] = Group[Node] => PathVals[A]
-
+ private[antixml] case class PathVal[+A](value: A, path: ZipperPath)
+
+ private[antixml] trait PathVals[+A] extends Traversable[PathVal[A]]
+
+ private[antixml] type PathFunction[+A] = (Group[Node]) => PathVals[A]
+
/** Alias for the internal bottom-up path used during selection. This must be reversed before
* being returned in a [[PathVal]].
*/
- private type BottomUp = List[Location]
+ private type BottomUp = List[Int]
- private def reverse(rev: BottomUp) = VectorCase.fromSeq(rev.reverse)
+ private def reverse(rev: BottomUp) = ZipperPath.reversed(rev)
/** A path function that selects on nodes in the given group. */
- def fromNodes[A](selector: Selector[A])(nodes: Group[Node]): PathVals[A] = {
- collectGroup(nodes, selector, Nil)
+ def fromNodes[A](selector: Selector[A])(nodes: Group[Node]): PathVals[A] = new PathVals[A] {
+ override def foreach[U] (f: (PathVal[A]) => U) {
+ forEachIn(nodes, selector, Nil, f)
+ }
}
- /** A path function that selects on the given nodes and recursively on the children (breadth first). */
- def all[A](selector: Selector[A])(nodes: Group[Node]): PathVals[A] = {
- collectGroupRecursive(nodes, Nil, selector)
+ /** A path function that selects on the given nodes and recursively on the children (depth first). */
+ def all[A](selector: Selector[A])(nodes: Group[Node]): PathVals[A] = new PathVals[A] {
+ override def foreach[U] (f: (PathVal[A]) => U) {
+ forEachInDeep(nodes, selector, Nil, f)
+ }
}
/**
* A path function that selects on the given nodes and recursively on the children, returning those
* matching nodes that are not themselves descendants of matching nodes. (depth first).
*/
- def allMaximal[A](selector: Selector[A])(nodes: Group[Node]): PathVals[A] = {
- collectMaximalRecursive(nodes, Nil, selector)
+ def allMaximal[A](selector: Selector[A])(nodes: Group[Node]): PathVals[A] = new PathVals[A] {
+ override def foreach[U] (f: (PathVal[A]) => U) {
+ forEachInDeepShortCircuit(nodes, selector, Nil, f)
+ }
}
/** A path function that selects on the children of the given group. */
- def directChildren[A](selector: Selector[A])(nodes: Group[Node]): PathVals[A] = {
- collectChildrenOfGroup(nodes, selector)
+ def directChildren[A](selector: Selector[A])(nodes: Group[Node]): PathVals[A] = new PathVals[A] {
+ override def foreach[U] (f: (PathVal[A]) => U) {
+ forEachChildIn(nodes, selector, Nil, f)
+ }
}
/** A path function that selects on the recursively on all the children of the given group (breadth first). */
- def allChildren[A](selector: Selector[A])(nodes: Group[Node]): PathVals[A] = {
- collectGroupChildren(nodes, Nil, selector) flatMap {case (g,p) => collectGroupRecursive(g,p,selector)}
+ def allChildren[A](selector: Selector[A])(nodes: Group[Node]): PathVals[A] = new PathVals[A] {
+ override def foreach[U] (f: (PathVal[A]) => U) {
+ forEachChildInDeep(nodes, selector, Nil, f)
+ }
}
/** A path function that returns all the matching children that are not descendants of matching children (depth first). */
- def allMaximalChildren[A](selector: Selector[A])(nodes: Group[Node]): PathVals[A] = {
- collectGroupChildren(nodes, Nil, selector) flatMap {case (g,p) => collectMaximalRecursive(g,p,selector)}
- }
-
- /** Collects items from the given group that match the selector. */
- private def collectGroup[A](nodes: Group[Node], s: Selector[A], p: BottomUp): PathVals[A] = {
- dispatchSelector(s, nodes) {
- val ni = nodes.zipWithIndex
- for ((n, i) <- ni if s isDefinedAt n) yield PathVal(s(n),reverse(i :: p))
+ def allMaximalChildren[A](selector: Selector[A])(nodes: Group[Node]): PathVals[A] = new PathVals[A] {
+ override def foreach[U] (f: (PathVal[A]) => U) {
+ forEachChildInDeepShortCircuit(nodes, selector, Nil, f)
}
}
- /** Collects items from the list groups that match the selector. */
- private def collectGroups[A](groups: Seq[(Group[Node], BottomUp)], s: Selector[A]): PathVals[A] = {
- groups flatMap {gp =>
- val (g, p) = gp
- collectGroup(g, s, p)
+ private def forEachIn[A,U](g: Group[Node], s: Selector[A], parentPath: BottomUp, f: PathVal[A] => U) {
+ if (dispatchSelector(s,g)) {
+ for(i <- 0 until g.length) {
+ val n = g(i)
+ if (s.isDefinedAt(n))
+ f(PathVal(s(n), reverse(i :: parentPath)))
+ }
}
}
- /** Applies the group selector collection function on the children of the given group. */
- private def collectChildrenOfGroupWith[A]
- (nodes: Group[Node], s: Selector[A], p: BottomUp)
- (toVals: (Group[Node], Selector[A], BottomUp) => PathVals[A]): PathVals[A] = {
- dispatchSelector(s, nodes) {
- val ni = nodes.zipWithIndex
- ni flatMap {
- case (e: Elem, i) => toVals(e.children, s, i :: p)
- case _ => Nil
+ private def forEachInDeep[A,U](g: Group[Node], s: Selector[A], parentPath: BottomUp, f: PathVal[A] => U) {
+ if (dispatchSelector(s,g)) {
+ for(i <- 0 until g.length) {
+ val n = g(i)
+ if (s.isDefinedAt(n))
+ f(PathVal(s(n), reverse(i :: parentPath)))
+ val ch = n.children
+ if (!ch.isEmpty)
+ forEachInDeep(ch, s, i :: parentPath, f)
}
}
}
- /** Collects items from the children of the given group that match the selector. */
- private def collectChildrenOfGroup[A](nodes: Group[Node], s: Selector[A]): PathVals[A] = {
- collectChildrenOfGroupWith(nodes, s, Nil) (collectGroup _)
- }
- /** Recursively collects the specified node if matches the selector, followed by its matching descendants (depth first) */
- private def collectNodeRecursive[A](n: Node, p: BottomUp, s: Selector[A]): PathVals[A] = {
- val rest = collectGroupRecursive(n.children, p, s)
- if (s.isDefinedAt(n)) PathVal(s(n), reverse(p)) +: rest
- else rest
- }
-
- /** Recursively collects items from the given group that match the selector. */
- private def collectGroupRecursive[A](group: Group[Node], p: BottomUp, s: Selector[A]): PathVals[A] = {
- if (group.isEmpty) Nil
- else
- dispatchSelector(s, group) {
- group.zipWithIndex flatMap {case (n,i) => collectNodeRecursive(n, i :: p, s)}
+ private def forEachInDeepShortCircuit[A,U](g: Group[Node], s: Selector[A], parentPath: BottomUp, f: PathVal[A] => U) {
+ if (dispatchSelector(s,g)) {
+ for(i <- 0 until g.length) {
+ val n = g(i)
+ if (s.isDefinedAt(n))
+ f(PathVal(s(n), reverse(i :: parentPath)))
+ else {
+ val ch = n.children
+ if (!ch.isEmpty)
+ forEachInDeepShortCircuit(ch, s, i :: parentPath, f)
+ }
}
+ }
}
- /**
- * Collects the specified node if matches the selector, otherwise collects its matching descendants that are
- * not themselves descendants of matching nodes (depth first).
- */
- private def collectMaximalRecursive[A](n: Node, p: BottomUp, s: Selector[A]): PathVals[A] = {
- if (s.isDefinedAt(n)) PathVal(s(n), reverse(p)) :: Nil
- else collectMaximalRecursive(n.children,p,s)
- }
-
- /** Recursively collects items from the given group that match the selector and are not descendants of matching items. */
- private def collectMaximalRecursive[A](group: Group[Node], p: BottomUp, s: Selector[A]): PathVals[A] = {
- if (group.isEmpty) Nil
- else
- dispatchSelector(s, group) {
- group.zipWithIndex flatMap {case (n,i) => collectMaximalRecursive(n, i :: p, s)}
+ private def forEachChildIn[A,U](g: Group[Node], s: Selector[A], parentPath: BottomUp, f: PathVal[A] => U) {
+ if (dispatchSelector(s,g)) {
+ for(i <- 0 until g.length) {
+ val ch = g(i).children
+ if (!ch.isEmpty)
+ forEachIn(ch,s, i::parentPath, f)
}
+ }
}
-
-
- /** Gathering all the children of the group that may match the selector. */
- private def collectGroupChildren(g: Group[Node], p: BottomUp, s: Selector[_]): Seq[(Group[Node], BottomUp)] = {
- dispatchSelector[Seq[(Group[Node], BottomUp)]](s, g)(Nil) {
- val gi = g.zipWithIndex
- gi flatMap {
- case (e: Elem, i) => Some((e.children, i :: p))
- case _ => None
+
+ private def forEachChildInDeep[A,U](g: Group[Node], s: Selector[A], parentPath: BottomUp, f: PathVal[A] => U) {
+ if (dispatchSelector(s,g)) {
+ for(i <- 0 until g.length) {
+ val ch = g(i).children
+ if (!ch.isEmpty)
+ forEachInDeep(ch,s, i::parentPath, f)
}
}
}
-
- /** If dispatching on the selector yields true, executing the given code block, otherwise returning
- * the default value.*/
- private def dispatchSelector[A](s: Selector[_], g: Group[Node])(default: A)(vals: => A): A = {
- if (dispatchSelector(s, g)) vals
- else default
- }
- /** If dispatching on the selector yields true, executing the given code block, otherwise returning
- * an empty list.
- */
- private def dispatchSelector[A](s: Selector[A], g: Group[Node])(vals: => PathVals[A]): PathVals[A] = {
- dispatchSelector[PathVals[A]](s, g)(Nil)(vals)
+ private def forEachChildInDeepShortCircuit[A,U](g: Group[Node], s: Selector[A], parentPath: BottomUp, f: PathVal[A] => U) {
+ if (dispatchSelector(s,g)) {
+ for(i <- 0 until g.length) {
+ val ch = g(i).children
+ if (!ch.isEmpty)
+ forEachInDeepShortCircuit(ch,s, i::parentPath, f)
+ }
+ }
}
-
+
/** Returns true if there is a chance that applying the given selector on the group
* would yield some results. */
private def dispatchSelector(s: Selector[_], g: Group[Node]) = {
@@ -159,7 +159,7 @@ trait Selectable[+A <: Node] {
*
* @usecase def \\!(selector: Selector[Node]): Zipper[Node]
*/
- def \\![B, That](selector: Selector[B])(implicit cbfwz: CanBuildFromWithZipper[Group[_ <: Node], B, That]): That = {
+ def \\![B, That](selector: Selector[B])(implicit cbfwz: CanBuildFromWithZipper[Group[A], B, That]): That = {
fromPathFunc(allMaximalChildren(selector), cbfwz)
}
@@ -177,7 +177,7 @@ trait Selectable[+A <: Node] {
*
* @usecase def select(selector: Selector[Node]): Zipper[Node]
*/
- def select[B, That](selector: Selector[B])(implicit cbfwz: CanBuildFromWithZipper[Group[_ <: Node], B, That]): That = {
+ def select[B, That](selector: Selector[B])(implicit cbfwz: CanBuildFromWithZipper[Group[A], B, That]): That = {
fromPathFunc(fromNodes(selector),cbfwz)
}
@@ -36,6 +36,8 @@ import scala.math.Ordering
class PathCreatorSpecs extends SpecificationWithJUnit {
+ def vec[A](t: Traversable[A]) = Vector(t.toSeq:_*)
+
val s = *
val x0 = fromString("<root0><a0>foo</a0><b0>baz</b0><c0/></root0>")
@@ -54,7 +56,7 @@ class PathCreatorSpecs extends SpecificationWithJUnit {
def ps(pars: (Elem, Int)*) = List(pars.map(_._2): _*) //List(pars.map(ParentLoc.tupled): _*)
def nl(n: Node, l: Int) = l //WithLoc(n, l)
- def pv(n: Node, path: Int*) = PathVal(n, Vector(path: _*))
+ def pv(n: Node, path: Int*) = PathVal(n, ZipperPath(path: _*))
val root = Vector(pv(x0,0), pv(x1, 1), pv(x2, 2))
val directChild = Vector (
@@ -70,47 +72,47 @@ class PathCreatorSpecs extends SpecificationWithJUnit {
"allMaximalChildren" should {
"stop at the highest match" in {
- allMaximalChildren(s)(group) mustEqual directChild.sortBy(pathKeys)
+ vec(allMaximalChildren(s)(group)) mustEqual directChild.sortBy(pathKeys)
}
"find deep matches" in {
val sel = Selector({case x:Text => x})
- allMaximalChildren(sel)(group) mustEqual rest.sortBy(pathKeys)
+ vec(allMaximalChildren(sel)(group)) mustEqual rest.sortBy(pathKeys)
}
"find matches at mixed levels" in {
val sel = Selector({
case e:Elem if e.name == "a0" => e
case t:Text => t
})
- allMaximalChildren(sel)(group) mustEqual (directChild.take(1) ++ rest.drop(1)).sortBy(pathKeys)
+ vec(allMaximalChildren(sel)(group)) mustEqual (directChild.take(1) ++ rest.drop(1)).sortBy(pathKeys)
}
}
"allMaximal" should {
"stop at the highest match" in {
- allMaximal(s)(group) mustEqual root.sortBy(pathKeys)
+ vec(allMaximal(s)(group)) mustEqual root.sortBy(pathKeys)
}
"find deep matches" in {
val sel = Selector({case x:Text => x})
- allMaximal(sel)(group) mustEqual rest.sortBy(pathKeys)
+ vec(allMaximal(sel)(group)) mustEqual rest.sortBy(pathKeys)
}
"find matches at mixed levels" in {
val sel = Selector({
case e:Elem if e.name == "a0" => e
case t:Text => t
})
- allMaximal(sel)(group) mustEqual (directChild.take(1) ++ rest.drop(1)).sortBy(pathKeys)
+ vec(allMaximal(sel)(group)) mustEqual (directChild.take(1) ++ rest.drop(1)).sortBy(pathKeys)
}
"find matches at mixed levels 2" in {
val sel = Selector({
case e:Elem if e.name == "root0" || e.name=="a0" || e.name=="a1" => e
case t:Text => t
})
- allMaximal(sel)(group) mustEqual (root.take(1) ++ directChild.drop(3).take(1) ++ rest.drop(3)).sortBy(pathKeys)
+ vec(allMaximal(sel)(group)) mustEqual (root.take(1) ++ directChild.drop(3).take(1) ++ rest.drop(3)).sortBy(pathKeys)
}
}
@@ -121,36 +123,36 @@ class PathCreatorSpecs extends SpecificationWithJUnit {
"ignore empty groups" in {
val empty = Group()
- fromNodes(s)(empty) mustEqual Nil
- all(s)(empty) mustEqual Nil
- directChildren(s)(empty) mustEqual Nil
- allChildren(s)(empty) mustEqual Nil
+ vec(fromNodes(s)(empty)) mustEqual Nil
+ vec(all(s)(empty)) mustEqual Nil
+ vec(directChildren(s)(empty)) mustEqual Nil
+ vec(allChildren(s)(empty)) mustEqual Nil
}
"take from the root of the nodes" in {
- fromNodes(s)(group) mustEqual root.sortBy(pathKeys)
+ vec(fromNodes(s)(group)) mustEqual root.sortBy(pathKeys)
}
"take the children of the root nodes" in {
- directChildren(s)(group) mustEqual directChild.sortBy(pathKeys)
+ vec(directChildren(s)(group)) mustEqual directChild.sortBy(pathKeys)
}
"take all the nodes recursively, depth first" in {
- all(s)(group) mustEqual (root ++ directChild ++ rest).sortBy(pathKeys)
+ vec(all(s)(group)) mustEqual (root ++ directChild ++ rest).sortBy(pathKeys)
}
"take all children nodes recursively, depth first" in {
- allChildren(s)(group) mustEqual (directChild ++ rest).sortBy(pathKeys)
+ vec(allChildren(s)(group)) mustEqual (directChild ++ rest).sortBy(pathKeys)
}
"apply selectors at the root level" in {
val sel = Selector({ case Elem(_, "root1", _, _, _) => elem("selected") })
- fromNodes(sel)(group) mustEqual Vector(pv(elem("selected"), 1))
+ vec(fromNodes(sel)(group)) mustEqual Vector(pv(elem("selected"), 1))
}
"apply selectors to the children of the root" in {
val sel = Selector({ case Elem(_, "b2", _, _, _) => elem("selected") })
- directChildren(sel)(group) mustEqual Vector(pv(elem("selected"),2,1))
+ vec(directChildren(sel)(group)) mustEqual Vector(pv(elem("selected"),2,1))
}
val selDeep = Selector({
@@ -171,11 +173,11 @@ class PathCreatorSpecs extends SpecificationWithJUnit {
)
"apply selectors recursively" in {
- all(selDeep)(group) mustEqual (selResRoot ++ selResNoRoot).sortBy(pathKeys)
+ vec(all(selDeep)(group)) mustEqual (selResRoot ++ selResNoRoot).sortBy(pathKeys)
}
"apply selectors recursively on the children" in {
- allChildren(selDeep)(group) mustEqual selResNoRoot.sortBy(pathKeys)
+ vec(allChildren(selDeep)(group)) mustEqual selResNoRoot.sortBy(pathKeys)
}
}
Oops, something went wrong. Retry.

0 comments on commit febd26a

Please sign in to comment.