alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Groovy example source code file (StreamingDOMBuilder.groovy)

This example Groovy source code file (StreamingDOMBuilder.groovy) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Groovy tags/keywords

abstractstreamingbuilder, basemarkupbuilder, buildable, buildable, closure, closure, dom, groovyruntimeexception, groovyruntimeexception, map, namespace, node, parser, streamingdombuilder, streamingdombuilder, uri, xml

The Groovy StreamingDOMBuilder.groovy source code

/*
 * Copyright 2003-2007 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 javax.xml.parsers.DocumentBuilderFactory
import org.w3c.dom.Node

import groovy.xml.streamingmarkupsupport.AbstractStreamingBuilder
import groovy.xml.streamingmarkupsupport.BaseMarkupBuilder

class StreamingDOMBuilder extends AbstractStreamingBuilder {
    def pendingStack = []
    def defaultNamespaceStack = [""]
    def commentClosure = {doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, dom ->
        def comment = dom.document.createComment(body)
        if (comment != null) {
            dom.element.appendChild(comment)
        }
    }
    def piClosure = {doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, dom ->
        attrs.each {target, instruction ->
            def pi = null
            if (instruction instanceof Map) {
                def buf = new StringBuffer()
                instruction.each { name, value ->
                    if (value.toString().contains('"')) {
                        buf.append(" $name='$value'")
                    } else {
                        buf.append(" $name=\"$value\"" )
                    }
                }
                pi = dom.document.createProcessingInstruction(target, buf.toString())
            } else {
                pi = dom.document.createProcessingInstruction(target, instruction)
            }
            if (pi != null) {
                dom.element.appendChild(pi)
            }
        }
    }
    def noopClosure = {doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, dom ->
        if (body instanceof Closure) {
            def body1 = body.clone()
            body1.delegate = doc
            body1(doc)
        } else if (body instanceof Buildable) {
            body.build(doc)
        } else if (body != null) {
            body.each {
                if (it instanceof Closure) {
                    def body1 = it.clone()
                    body1.delegate = doc
                    body1(doc)
                } else if (it instanceof Buildable) {
                    it.build(doc)
                } else {
                    dom.element.appendChild(dom.document.createTextNode(it))
                }
            }
        }
    }
    def tagClosure = {tag, doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, dom ->
        def attributes = []
        def nsAttributes = []
        def defaultNamespace = defaultNamespaceStack.last()

        attrs.each {key, value ->
            if (key.contains('$')) {
                def parts = key.tokenize('$')
                def namespaceUri = null

                if (namespaces.containsKey(parts[0])) {
                    namespaceUri = namespaces[parts[0]]

                    nsAttributes.add([namespaceUri, "${parts[0]}:${parts[1]}", "$value"])

                } else {
                    throw new GroovyRuntimeException("bad attribute namespace tag in ${key}")
                }
            } else {
                attributes.add([key, value])
            }
        }

        def hiddenNamespaces = [:]

        pendingNamespaces.each {key, value ->
            if (key == ':') {
                defaultNamespace = "$value"
                nsAttributes.add(["http://www.w3.org/2000/xmlns/", "xmlns", defaultNamespace])
            } else {
                hiddenNamespaces[key] = namespaces[key]
                namespaces[key] = value
                nsAttributes.add(["http://www.w3.org/2000/xmlns/", "xmlns:${key}", "$value"])
            }
        }

        // setup the tag info

        def uri = defaultNamespace
        def qualifiedName = tag

        if (prefix != "") {
            if (namespaces.containsKey(prefix)) {
                uri = namespaces[prefix]
            } else if (pendingNamespaces.containsKey(prefix)) {
                uri = pendingNamespaces[prefix]
            } else {
                throw new GroovyRuntimeException("Namespace prefix: ${prefix} is not bound to a URI")
            }
            if (prefix != ":") {
                qualifiedName = prefix + ":" + tag
            }
        }

        def element = dom.document.createElementNS(uri, qualifiedName)

        nsAttributes.each {
            element.setAttributeNS(it[0], it[1], it[2])
        }
        attributes.each {
            element.setAttribute(it[0], it[1])
        }

        dom.element.appendChild(element)
        dom.element = element

        if (body != null) {
            defaultNamespaceStack.push defaultNamespace
            pendingStack.add pendingNamespaces.clone()
            pendingNamespaces.clear()

            if (body instanceof Closure) {
                def body1 = body.clone()

                body1.delegate = doc
                body1(doc)
            } else if (body instanceof Buildable) {
                body.build(doc)
            } else {
                body.each {
                    if (it instanceof Closure) {
                        def body1 = it.clone()

                        body1.delegate = doc
                        body1(doc)
                    } else if (it instanceof Buildable) {
                        it.build(doc)
                    } else {
                        dom.element.appendChild(dom.document.createTextNode(it))
                    }
                }
            }

            pendingNamespaces.clear()
            pendingNamespaces.putAll pendingStack.pop()
            defaultNamespaceStack.pop()
        }

        dom.element = dom.element.getParentNode()

        hiddenNamespaces.each { key, value ->
            if (value == null) namespaces.remove key
            else namespaces[key] = value
        }
    }

    def builder = null

    StreamingDOMBuilder() {
        specialTags.putAll(['yield':noopClosure,
                            'yieldUnescaped':noopClosure,
                            'comment':commentClosure,
                            'pi':piClosure])
        def nsSpecificTags = [':'                                          : [tagClosure, tagClosure, [:]],    // the default namespace
                          'http://www.w3.org/2000/xmlns/'                  : [tagClosure, tagClosure, [:]],
                          'http://www.codehaus.org/Groovy/markup/keywords' : [badTagClosure, tagClosure, specialTags]]
        this.builder = new BaseMarkupBuilder(nsSpecificTags)
    }

    def bind(closure) {
        def boundClosure = this.builder.bind(closure)
        return {
            if (it instanceof Node) {
                def document = it.getOwnerDocument()
                boundClosure.trigger = ['document' : document, 'element' : it]
                return document
            } else {
                def dBuilder = DocumentBuilderFactory.newInstance()
                dBuilder.namespaceAware = true
                def newDocument = dBuilder.newDocumentBuilder().newDocument()
                boundClosure.trigger = ['document' : newDocument, 'element' : newDocument]
                return newDocument
            }
        }
    }
}

Other Groovy examples (source code examples)

Here is a short list of links related to this Groovy StreamingDOMBuilder.groovy source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.