001    /*
002     $Id: DOMBuilder.java 4132 2006-10-18 08:24:58Z paulk $
003    
004     Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005    
006     Redistribution and use of this software and associated documentation
007     ("Software"), with or without modification, are permitted provided
008     that the following conditions are met:
009    
010     1. Redistributions of source code must retain copyright
011        statements and notices.  Redistributions must also contain a
012        copy of this document.
013    
014     2. Redistributions in binary form must reproduce the
015        above copyright notice, this list of conditions and the
016        following disclaimer in the documentation and/or other
017        materials provided with the distribution.
018    
019     3. The name "groovy" must not be used to endorse or promote
020        products derived from this Software without prior written
021        permission of The Codehaus.  For written permission,
022        please contact info@codehaus.org.
023    
024     4. Products derived from this Software may not be called "groovy"
025        nor may "groovy" appear in their names without prior written
026        permission of The Codehaus. "groovy" is a registered
027        trademark of The Codehaus.
028    
029     5. Due credit should be given to The Codehaus -
030        http://groovy.codehaus.org/
031    
032     THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
033     ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
034     NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035     FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
036     THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
037     INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
039     SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
040     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
041     STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
042     ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
043     OF THE POSSIBILITY OF SUCH DAMAGE.
044    
045     */
046    package groovy.xml;
047    
048    import groovy.util.BuilderSupport;
049    
050    import java.io.IOException;
051    import java.io.Reader;
052    import java.util.Iterator;
053    import java.util.Map;
054    
055    import javax.xml.parsers.DocumentBuilder;
056    import javax.xml.parsers.DocumentBuilderFactory;
057    import javax.xml.parsers.ParserConfigurationException;
058    
059    import org.w3c.dom.Document;
060    import org.w3c.dom.Element;
061    import org.w3c.dom.Node;
062    import org.xml.sax.InputSource;
063    import org.xml.sax.SAXException;
064    
065    /**
066     * A helper class for creating a W3C DOM tree
067     *
068     * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
069     * @version $Revision: 4132 $
070     */
071    public class DOMBuilder extends BuilderSupport {
072    
073        Document document;
074        DocumentBuilder documentBuilder;
075    
076        public static DOMBuilder newInstance() throws ParserConfigurationException {
077            return newInstance(false, true);
078        }
079    
080        public static DOMBuilder newInstance(boolean validating, boolean namespaceAware) throws ParserConfigurationException {
081            DocumentBuilderFactory factory = FactorySupport.createDocumentBuilderFactory();
082            factory.setNamespaceAware(namespaceAware);
083            factory.setValidating(validating);
084            return new DOMBuilder(factory.newDocumentBuilder());
085        }
086    
087        public static Document parse(Reader reader) throws SAXException, IOException, ParserConfigurationException {
088            return parse(reader, false, true);
089        }
090    
091        public static Document parse(Reader reader, boolean validating, boolean namespaceAware)
092                throws SAXException, IOException, ParserConfigurationException {
093            DocumentBuilderFactory factory = FactorySupport.createDocumentBuilderFactory();
094            factory.setNamespaceAware(namespaceAware);
095            factory.setValidating(validating);
096            DocumentBuilder documentBuilder = factory.newDocumentBuilder();
097            return documentBuilder.parse(new InputSource(reader));
098        }
099    
100        public DOMBuilder(Document document) {
101            this.document = document;
102        }
103    
104        public DOMBuilder(DocumentBuilder documentBuilder) {
105            this.documentBuilder = documentBuilder;
106        }
107    
108        protected void setParent(Object parent, Object child) {
109            Node current = (Node) parent;
110            Node node = (Node) child;
111    
112            current.appendChild(node);
113        }
114    
115        protected Object createNode(Object name) {
116            if (document == null) {
117                document = createDocument();
118            }
119            if (name instanceof QName) {
120                QName qname = (QName) name;
121                return document.createElementNS(qname.getNamespaceURI(), qname.getQualifiedName());
122            } else {
123                return document.createElement(name.toString());
124            }
125        }
126    
127        protected Document createDocument() {
128            if (documentBuilder == null) {
129                throw new IllegalArgumentException("No Document or DOMImplementation available so cannot create Document");
130            } else {
131                return documentBuilder.newDocument();
132            }
133        }
134    
135        protected Object createNode(Object name, Object value) {
136            Element element = (Element) createNode(name);
137            element.appendChild(document.createTextNode(value.toString()));
138            return element;
139        }
140    
141        protected Object createNode(Object name, Map attributes, Object value) {
142            Element element = (Element) createNode(name, attributes);
143            element.appendChild(document.createTextNode(value.toString()));
144            return element;
145        }
146    
147        protected Object createNode(Object name, Map attributes) {
148            Element element = (Element) createNode(name);
149            for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
150                Map.Entry entry = (Map.Entry) iter.next();
151                String attrName = entry.getKey().toString();
152                Object value = entry.getValue();
153                if ("xmlns".equals(attrName)) {
154                    if (value instanceof Map) {
155                        appendNamespaceAttributes(element, (Map) value);
156                    } else {
157                        throw new IllegalArgumentException("The value of the xmlns attribute must be a Map of QNames to String URIs");
158                    }
159                } else {
160                    // TODO handle null values and treat as ''
161                    element.setAttribute(attrName, value.toString());
162                }
163            }
164            return element;
165        }
166    
167        protected void appendNamespaceAttributes(Element element, Map attributes) {
168            for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
169                Map.Entry entry = (Map.Entry) iter.next();
170                Object key = entry.getKey();
171                Object value = entry.getValue();
172                if (value == null) {
173                    throw new IllegalArgumentException("The value of key: " + key + " cannot be null");
174                }
175                if (key instanceof String) {
176                    String prefix = (String) key;
177    
178                    //System.out.println("Creating namespace for prefix: " + prefix + " with value: " + value);
179    
180                    //element.setAttributeNS("http://www.w3.org/XML/1998/namespace", "xmlns:" + prefix, value.toString());
181                    element.setAttributeNS("", prefix, value.toString());
182                } else if (key instanceof QName) {
183                    QName qname = (QName) key;
184                    element.setAttributeNS(qname.getNamespaceURI(), qname.getQualifiedName(), value.toString());
185                } else {
186                    throw new IllegalArgumentException("The key: " + key + " should be an instanceof of " + QName.class);
187                }
188            }
189        }
190    }