blob: 62882b45ce7df5f83362838c31991a849aaf8b67 [file] [log] [blame]
package groovy.xml
import groovy.xml.streamingmarkupsupport.AbstractStreamingBuilder
import groovy.xml.streamingmarkupsupport.StreamingMarkupWriter
import groovy.xml.streamingmarkupsupport.BaseMarkupBuilder
class IndentedStreamingMarkupBuilder extends StreamingMarkupBuilder {
def tagIndentOffset = ' '
def attributeIndentOffest = ' '
private def tagIndentString = ''
private def attributeIndentString = attributeIndentOffest
private def tagIndentclosed = false
def pendingStack = []
def commentClosure = {doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, out ->
out.unescaped() << "\n${tagIndentString}<!--"
out.escaped() << body
out.unescaped() << "${tagIndentString}-->"
}
def piClosure = {doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, out ->
attrs.each {target, instruction ->
out.unescaped() << "\n${tagIndentString}<?"
if (instruction instanceof Map) {
out.unescaped() << target
instruction.each {name, value ->
if (value.toString().contains('"')) {
out.unescaped() << " $name='$value'"
} else {
out.unescaped() << " $name=\"$value\""
}
}
} else {
out.unescaped() << "$target $instruction"
}
out.unescaped() << "?>"
}
}
def declarationClosure = {doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, out ->
out.unescaped() << '<?xml version="1.0"'
if (out.encodingKnown) out.escaped() << " encoding=\"${out.encoding}\""
out.unescaped() << '?>'
}
def noopClosure = {doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, out ->
body.each {
if (it instanceof Closure) {
def body1 = it.clone()
body1.delegate = doc
body1(doc)
} else if (it instanceof Buildable) {
it.build(doc)
} else {
out.escaped() << it
}
}
}
def unescapedClosure = {doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, out ->
out.unescaped() << body
}
def tagClosure = {tag, doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, out ->
if (prefix != "") {
if (!(namespaces.containsKey(prefix) || pendingNamespaces.containsKey(prefix))) {
throw new GroovyRuntimeException("Namespace prefix: ${prefix} is not bound to a URI")
}
if (prefix != ":") tag = prefix + ":" + tag
}
out = out.unescaped() << "\n${tagIndentString}<${tag}"
attrs.each {key, value ->
if (key.contains('$')) {
def parts = key.tokenize('$')
if (namespaces.containsKey(parts[0]) || pendingNamespaces.containsKey(parts[0])) {
key = parts[0] + ":" + parts[1]
} else {
throw new GroovyRuntimeException("bad attribute namespace tag: ${parts[0]} in ${key}")
}
}
out << " ${key}='"
out.writingAttribute = true
"${value}".build(doc)
out.writingAttribute = false
out << "'"
}
def hiddenNamespaces = [:]
pendingNamespaces.each {key, value ->
hiddenNamespaces[key] = namespaces[key]
namespaces[key] = value
out << ((key == ":") ? " xmlns='" : " xmlns:${key}='")
out.writingAttribute = true
"${value}".build(doc)
out.writingAttribute = false
out << "'"
}
if (body == null) {
out << "/>"
} else {
out << ">"
tagIndentclosed = false
tagIndentString = tagIndentString + tagIndentOffset
attributeIndentString = tagIndentString + attributeIndentOffest
pendingStack.add pendingNamespaces.clone()
pendingNamespaces.clear()
body.each {
if (it instanceof Closure) {println"CLOSURE"
def body1 = it.clone()
body1.delegate = doc
body1(doc)
} else if (it instanceof Buildable) {println"BUILDABLE}"
it.build(doc)
} else {println"ESCAPED}"
out.escaped() << it
}
}
System.out.println"XXX ${tag} XX${body?.dump()}"
pendingNamespaces.clear()
pendingNamespaces.putAll pendingStack.pop()
tagIndentString = tagIndentString - tagIndentOffset
attributeIndentString = tagIndentString + attributeIndentOffest
out << "${tagIndentclosed?'\n'+tagIndentString:''}</${tag}>"
tagIndentclosed = true
}
hiddenNamespaces.each {key, value ->
if (value == null) {
namespaces.remove key
} else {
namespaces[key] = value
}
}
}
def builder = null
IndentedStreamingMarkupBuilder() {
specialTags.putAll(['yield': noopClosure,
'yieldUnescaped': unescapedClosure,
'xmlDeclaration': declarationClosure,
'comment': commentClosure,
'pi': piClosure])
def nsSpecificTags = [':': [tagClosure, tagClosure, [:]], // the default namespace
'http://www.w3.org/XML/1998/namespace': [tagClosure, tagClosure, [:]],
'http://www.codehaus.org/Groovy/markup/keywords': [badTagClosure, tagClosure, specialTags]]
this.builder = new BaseMarkupBuilder(nsSpecificTags)
}
def encoding = null
public bind(closure) {
def boundClosure = this.builder.bind(closure);
def enc = encoding; // take a snapshot of the encoding when the closure is bound to the builder
{out ->
out = new StreamingMarkupWriter(out, enc)
boundClosure.trigger = out
out.flush()
}.asWritable()
}
}