Differentiate between XML breadthFirst() traversal and * navigation (minor 
tweaks: closes #517)


Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/3b77da35
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/3b77da35
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/3b77da35

Branch: refs/heads/master
Commit: 3b77da35512d157c67e8d3e9185779ab4603d051
Parents: 2e3cd26
Author: paulk <[email protected]>
Authored: Sat Apr 22 16:04:09 2017 +1000
Committer: paulk <[email protected]>
Committed: Sat Apr 22 16:04:09 2017 +1000

----------------------------------------------------------------------
 .../groovy-xml/src/spec/doc/xml-userguide.adoc  | 51 ++++++++++++++------
 .../spec/test/UserGuideXmlSlurperTest.groovy    | 23 +++++++--
 2 files changed, 54 insertions(+), 20 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/3b77da35/subprojects/groovy-xml/src/spec/doc/xml-userguide.adoc
----------------------------------------------------------------------
diff --git a/subprojects/groovy-xml/src/spec/doc/xml-userguide.adoc 
b/subprojects/groovy-xml/src/spec/doc/xml-userguide.adoc
index c7fb1d7..84a9cce 100644
--- a/subprojects/groovy-xml/src/spec/doc/xml-userguide.adoc
+++ b/subprojects/groovy-xml/src/spec/doc/xml-userguide.adoc
@@ -219,30 +219,32 @@ the
 
 Both of them are equally valid.
 
-=== Speed things up with * and ** navigation
+=== Flexible navigation with children (\*), depthFirst (**) and breadthFirst
 
-If you ever have used XPath you may have used expressions like
+If you ever have used XPath, you may have used expressions like:
 
-* `//` : Look everywhere
 * `/following-sibling::othernode` : Look for a node "othernode" in the same 
level
+* `//` : Look everywhere
 
-More or less we have their counterparts in `GPath` with the shortcuts `*` and 
`**`.
+More or less we have their counterparts in GPath with the shortcuts `\*` (aka 
`children()`) and `**` (aka `depthFirst()`).
 
 The first example shows a simple use of `*`, which only iterates over the 
direct children of the node.
 
 [source,groovy]
 .Using *
 ----
-include::{rootProjectDir}/subprojects/groovy-xml/src/spec/test/UserGuideXmlSlurperTest.groovy[tags=testBreadthFirst1,indent=0]
+include::{rootProjectDir}/subprojects/groovy-xml/src/spec/test/UserGuideXmlSlurperTest.groovy[tags=testChildren,indent=0]
 ----
 
-This test searches for any node at the same level of the "books" node
-first. This operation roughly corresponds to the `breadthFirst()` method, 
except that it only stops at *one level* instead of continuing to the inner 
levels.
+This test searches for any child nodes of the "books" node matching the given
+condition. In a bit more detail, the expression says: _Look for any node with
+a tag name equal to 'book' having an id with a value of '2' directly under
+the 'books' node_.
 
-The expression says *_Look for any node with a tag name
-equals 'book' having an id with a value of '2' directly under the 'books' 
node_*.
+This operation roughly corresponds to the `breadthFirst()` method, except that
+it only stops at *one level* instead of continuing to the inner levels.
 
-But what if we would like to look for a given value
+What if we would like to look for a given value
 without having to know exactly where it is. Let's say that the
 only thing we know is the id of the author "Lewis Carroll" . How are
 we going to be able to find that book? Using `**` is the solution:
@@ -253,20 +255,37 @@ we going to be able to find that book? Using `**` is the 
solution:
 
include::{rootProjectDir}/subprojects/groovy-xml/src/spec/test/UserGuideXmlSlurperTest.groovy[tags=testDepthFirst1,indent=0]
 ----
 
-`**` is the same as looking something *everywhere in the
-tree from this point down*. In this case we've used the method
-`find(Closure cl)` to find just the first occurrence. `**` corresponds to the 
`depthFirst()` method.
+`**` is the same as looking for something _everywhere in the
+tree from this point down_. In this case, we've used the method
+`find(Closure cl)` to find just the first occurrence.
 
-What if we want to collect all book's titles?
+What if we want to collect all book's titles? That's easy, just use `findAll`:
 
 [source,groovy]
-.depthFirst()
 ----
 
include::{rootProjectDir}/subprojects/groovy-xml/src/spec/test/UserGuideXmlSlurperTest.groovy[tags=testDepthFirst2,indent=0]
 ----
 
+In the last two examples, `**` is used as a shortcut for the `depthFirst()`
+method. It goes as far down the tree as it can while navigating down the
+tree from a given node. The `breadthFirst()` method finishes off all nodes
+on a given level before traversing down to the next level.
+
+The following example shows the difference between these two methods:
+
+[source,groovy]
+.depthFirst() vs .breadthFirst
+----
+include::{rootProjectDir}/subprojects/groovy-xml/src/spec/test/UserGuideXmlSlurperTest.groovy[tags=testDepthVsBreadth,indent=0]
+----
+
+In this example, we search for any nodes with an id attribute with value 2 or 
3.
+There are both `book` and `author` nodes that match that criteria. The 
different
+traversal orders will find the same nodes in each case but in different orders
+corresponding to how the tree was traversed.
+
 It is worth mentioning again that there are some useful methods
-converting a node's value to an integer, float... etc. Those methods
+converting a node's value to an integer, float, etc. Those methods
 could be convenient when doing comparisons like this:
 
 [source,groovy]

http://git-wip-us.apache.org/repos/asf/groovy/blob/3b77da35/subprojects/groovy-xml/src/spec/test/UserGuideXmlSlurperTest.groovy
----------------------------------------------------------------------
diff --git 
a/subprojects/groovy-xml/src/spec/test/UserGuideXmlSlurperTest.groovy 
b/subprojects/groovy-xml/src/spec/test/UserGuideXmlSlurperTest.groovy
index 9d5901f..13e7252 100644
--- a/subprojects/groovy-xml/src/spec/test/UserGuideXmlSlurperTest.groovy
+++ b/subprojects/groovy-xml/src/spec/test/UserGuideXmlSlurperTest.groovy
@@ -94,23 +94,25 @@ class UserGuideXmlSlurperTest  extends GroovyTestCase {
         // end::testGettingAnAttributeText[]
     }
 
-    void testBreadthFirst1() {
-        // tag::testBreadthFirst1[]
+    void testChildren() {
+        // tag::testChildren[]
         def response = new XmlSlurper().parseText(books)
 
+        // .'*' could be replaced by .children()
         def catcherInTheRye = response.value.books.'*'.find { node->
-         /* node.@id == 2 could be expressed as node['@id'] == 2 */
+            // node.@id == 2 could be expressed as node['@id'] == 2
             node.name() == 'book' && node.@id == '2'
         }
 
         assert catcherInTheRye.title.text() == 'Catcher in the Rye'
-        // end::testBreadthFirst1[]
+        // end::testChildren[]
     }
 
     void testDepthFirst1() {
         // tag::testDepthFirst1[]
         def response = new XmlSlurper().parseText(books)
 
+        // .'**' could be replaced by .depthFirst()
         def bookId = response.'**'.find { book->
             book.author.text() == 'Lewis Carroll'
         }.@id
@@ -129,6 +131,19 @@ class UserGuideXmlSlurperTest  extends GroovyTestCase {
         // end::testDepthFirst2[]
     }
 
+    void testDepthVsBreadth() {
+        // tag::testDepthVsBreadth[]
+        def response = new XmlSlurper().parseText(books)
+        def nodeName = { node -> node.name() }
+        def withId2or3 = { node -> node.@id in [2, 3] }
+
+        assert ['book', 'author', 'book', 'author'] ==
+                
response.value.books.depthFirst().findAll(withId2or3).collect(nodeName)
+        assert ['book', 'book', 'author', 'author'] ==
+                
response.value.books.breadthFirst().findAll(withId2or3).collect(nodeName)
+        // end::testDepthVsBreadth[]
+    }
+
     void testHelpers() {
         // tag::testHelpers[]
         def response = new XmlSlurper().parseText(books)

Reply via email to