/*
* Copyright 2003-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package groovy.xml
import groovy.util.slurpersupport.GPathResult
import org.custommonkey.xmlunit.XMLUnit
import org.custommonkey.xmlunit.Diff
/**
* @author Paul King
*/
class GpathSyntaxTestSupport {
private static sampleXml = '''
cheese
sleep
1
y
http://example.org/
http://example.org/
'''
private static nestedXml = '''
'''
static void checkElement(Closure getRoot) {
def root = getRoot(sampleXml)
assert root != null
def characters = root.character
assert 2 == characters.size()
assert 2 == root.'character'.size()
assert 2 == root['character'].size()
def wallace = characters[0]
assert wallace.name() == 'character'
def likes = characters.likes
assert 2 == likes.size()
def wallaceLikes = likes[0]
assert wallaceLikes.name() == 'likes'
assert wallaceLikes.text() == 'cheese'
checkEmptyMissingCases(root)
if (isDom(root)) {
// additional DOM long-hand syntax
// for illustrative purposes only
assert likes.item(0).nodeName == 'likes'
assert wallaceLikes.firstChild.nodeValue == 'cheese'
if (wallaceLikes.class.name.contains('xerces')) {
assert 'cheese' == wallaceLikes.textContent
}
}
}
private static void checkEmptyMissingCases(root) {
def unknownChild = root.xxx
assert unknownChild.isEmpty()
def unknownAttr = root.'@xxx'
assert isSlurper(root) || !unknownAttr
assert !isSlurper(root) || unknownAttr.isEmpty()
assert root.'empty'.text() == ''
}
static void checkFindElement(Closure getRoot) {
def root = getRoot(sampleXml)
// let's find Gromit
def gromit = root.character.find { it.'@id' == '2' }
assert gromit != null, "Should have found Gromit!"
assert gromit['@name'] == "Gromit"
// let's find what Wallace likes in 1 query
def answer = root.character.find { it['@id'] == '1' }.likes[0].text()
assert answer == "cheese"
}
static void checkNestedSizeExpressions(Closure getRoot) {
def root = getRoot(nestedXml)
assert root.'*'.size() == 4, "Expected size 4 but was ${root.'*'.size()}"
assert root.'a'.'z'.size() == 2, "Expected size 2 but was ${root.'a'.'z'.size()}"
assert root.'*'.'*'.size() == 5
assert root.a.'*'.size() == 3
assert root.'*'.z.size() == 3
assert root.'*'.'*'.collect{it.name()} == ["z", "z", "y", "z", "x"]
assert root.a.'*'.collect{it.name()} == ["z", "z", "y"]
assert root.'*'.findAll{ it.z.size() > 0 }.collect{it.name()} == ["a", "b"]
}
static void checkElementTypes(Closure getRoot) {
def root = getRoot(sampleXml)
def numericValue = root.numericValue[0]
def booleanValue = root.booleanValue[0]
def uriValue = root.uriValue[0]
def urlValue = root.urlValue[0]
assert numericValue.text().toInteger() == 1
assert numericValue.text().toLong() == 1
assert numericValue.text().toFloat() == 1
assert numericValue.text().toDouble() == 1
assert numericValue.text().toBigInteger() == 1
assert numericValue.text().toBigDecimal() == 1
assert booleanValue.text().toBoolean()
assert uriValue.text().toURI() == "http://example.org/".toURI()
assert urlValue.text().toURL() == "http://example.org/".toURL()
if (isSlurper(root)) {
// slurper shorthand - are these really pulling their weight?
assert numericValue.toInteger() == 1
assert numericValue.toLong() == 1
assert numericValue.toFloat() == 1
assert numericValue.toDouble() == 1
assert numericValue.toBigInteger() == 1
assert numericValue.toBigDecimal() == 1
assert booleanValue.toBoolean()
assert uriValue.toURI() == "http://example.org/".toURI()
assert urlValue.toURL() == "http://example.org/".toURL()
}
}
static void checkElementClosureInteraction(Closure getRoot) {
def root = getRoot(sampleXml)
def sLikes = root.character.likes.findAll{ it.text().startsWith('s') }
assert sLikes.size() == 1
assert root.likes.size() == 0
if (isDom(root)) {
// additional DOMCategory long-hand notation gets nested nodes from root
assert root.getElementsByTagName('likes').size() == 2
}
assert 'sleep' == sLikes[0].text()
assert 'cheesesleep' == root.character.likes.collect{ it.text() }.join()
assert root.character.likes.every{ it.text().contains('ee') }
def groupLikesByFirstLetter
def likes = root.character.likes.collect{ it }
if (isSlurper(root)) {
groupLikesByFirstLetter = likes.groupBy{ like ->
root.character.find{ it.likes[0].text() == like.text() }.@name.toString()[0]
}
// TODO: Broken? Why doesn't below work?
//groupLikesByFirstLetter = likes.groupBy{ it.parent().@name.toString()[0] }
} else {
groupLikesByFirstLetter = likes.groupBy{ it.parent().'@name'[0] }
}
groupLikesByFirstLetter.keySet().each{
groupLikesByFirstLetter[it] = groupLikesByFirstLetter[it][0].text()
}
assert groupLikesByFirstLetter == [W:'cheese', G:'sleep']
}
static void checkElementTruth(Closure getRoot) {
def root = getRoot(sampleXml)
assert root.character[0]
assert !root.doesnotexist[0]
}
static void checkAttribute(Closure getRoot) {
def root = getRoot(sampleXml)
if (isSlurper(root)) {
assert 'Wallace' == root.character[0].'@name'.text()
assert 'Wallace' == root.character[0]['@name'].text()
assert 'Wallace' == (root.character.'@name')[0].text()
assert ['Wallace', 'Gromit'] == root.character.'@name'.list()*.text()
assert 'WallaceGromit' == root.character.'@name'.text()
} else {
assert 'Wallace' == root.character[0].'@name'
assert 'Wallace' == root.character[0]['@name']
assert 'Wallace' == (root.character.'@name')[0]
assert ['Wallace', 'Gromit'] == root.character.collect{ it.'@name' }
assert 'WallaceGromit' == root.character.'@name'.join()
}
if (isSlurper(root)) {
// additional slurper shorthand
assert 'Wallace' == root.character[0].@name.text()
def gromit = root.character.find{ it.@id == '2' }
assert gromit.@name.name() == "name"
}
if (isParser(root)) {
// additional parser shorthand
assert 'Wallace' == root.character[0].@name
def gromit = root.character.find {it.@id == '2'}
def actualName = gromit.@name
assert actualName == 'Gromit'
}
if (isSlurper(root)) {
// validate the behavior of the '.name()' method.
root.character.each {
assert 'id' == it.@id.name()
assert 'name' == it.@name.name()
}
root.character.@id.each {
assert 'id' == it.name()
}
root.character.@name.each {
assert 'name' == it.name()
}
}
}
static void checkAttributes(Closure getRoot) {
def root = getRoot(sampleXml)
def attributes = root.character[0].attributes()
assert 2 == attributes.size()
assert 'Wallace' == attributes['name']
assert 'Wallace' == attributes.name
assert '1' == attributes.'id'
}
static void checkAttributeTruth(Closure getRoot) {
def root = getRoot(sampleXml)
if (isDom(root)) {
// no native attribute syntax for dom, so use quotes
assert root.character.findAll { it.'@id' == '2' }[0]
assert !root.character.findAll { it.'@id' == '3' }[0]
} else {
assert root.character.findAll { it.@id == '2' }[0]
assert !root.character.findAll { it.@id == '3' }[0]
}
}
static void checkChildren(Closure getRoot) {
def root = getRoot(sampleXml)
def children = root.children()
// count direct children
assert children.size() == 7, "Children ${children.size()}"
assert root.'*'.size() == 7
// illustrative purposes only
if (isDom(root)) {
// count whitespace and nested children
assert root.childNodes.size() == 15
// count nested children
assert root.getElementsByTagName('*').size() == 9
}
}
static void checkParent(Closure getRoot) {
def root = getRoot(sampleXml)
def gromit = root.character.find { it['@id'] == '2' }
assert gromit.likes[0].parent() == gromit
assert gromit.likes[0].'..' == gromit
assert gromit.likes[0].parent().parent() == root
assert gromit.parent() == root
if (isSlurper(root)) {
// additional slurper shorthand
assert gromit.likes.parent() == gromit
}
if (isSlurper(root)) {
assert root.parent() == root
} else if (isParser(root)) {
assert root.parent() == null
} else if (isDom(root)) {
assert (root.parent() instanceof org.w3c.dom.Document)
}
}
static void checkNegativeIndices(Closure getRoot) {
def root = getRoot('B1B2B3')
assert root.b[-1].text() == 'B3'
assert root.b[-3].text() == 'B1'
assert root.c.n[-1].'@id' == 'n2'
assert root.c.n[-2].'@id' == 'n1'
}
static void checkRangeIndex(Closure getRoot) {
def root = getRoot('B1B2B3')
def bs = root.b[1..2]
assert bs.size() == 2
assert bs[0].text() == 'B2'
assert bs[1].text() == 'B3'
bs = root.b[1..-1]
assert bs.size() == 2
assert bs[0].text() == 'B2'
assert bs[1].text() == 'B3'
bs = root.b[-3..-2]
assert bs.size() == 2
assert bs[0].text() == 'B1'
assert bs[1].text() == 'B2'
// Reverse order.
bs = root.b[2..1]
assert bs.size() == 2
assert bs[0].text() == 'B3'
assert bs[1].text() == 'B2'
bs = root.b[-2..-3]
assert bs.size() == 2
assert bs[0].text() == 'B2'
assert bs[1].text() == 'B1'
bs = root.b[1..-3]
assert bs.size() == 2
assert bs[0].text() == 'B2'
assert bs[1].text() == 'B1'
}
static void checkReplaceNode(Closure getRoot) {
def root = getRoot('B1B2500')
def r = root.c.replaceNode {
n(type: 'string') {
hello('world')
}
}
String result
// TODO remove need for cast
if (isSlurper(root)) result = XmlUtil.serialize((GPathResult)root)
else result = XmlUtil.serialize(root)
def expected = '''\
B1
B2
world
'''
XMLUnit.ignoreWhitespace = true
def xmlDiff = new Diff(result, expected)
assert xmlDiff.similar(), xmlDiff.toString()
// XmlSlurper replacements are deferred so can't check here
if (!isSlurper(root)) {
assert r.name() == 'n'
assert r.'@type' == 'string'
assert r.hello.text() == 'world'
}
// TODO behaviour when multiple nodes added?
// root.c.replaceNode {
// n(type: 'string') {
// hello('world')
// }
// n(type: 'int', 330)
// }
// TODO behavior when replaceNode() is operating on a node list?
// root.b.replaceNode {
// n(type: 'string') {
// hello('world')
// }
// }
}
static void checkPlus(Closure getRoot) {
def root = getRoot('B1B2C')
root.c + {
n(type: 'string') {
hello('world')
}
}
root.b + {
b('B3')
}
root.b[-1] + {
b('B4')
}
String result
// TODO remove need for cast
if (isSlurper(root)) result = XmlUtil.serialize((GPathResult)root)
else result = XmlUtil.serialize(root)
def expected = '''\
B1
B3
B2
B3
B4
C
world
'''
XMLUnit.ignoreWhitespace = true
def xmlDiff = new Diff(result, expected)
assert xmlDiff.similar(), xmlDiff.toString()
}
private static boolean isSlurper(node) {
return node.getClass().name.contains('slurper')
}
private static boolean isParser(node) {
return (node instanceof groovy.util.Node)
}
private static boolean isDom(node) {
return node.getClass().name.contains('Element')
}
}