001    /*
002     $Id: DOMBuilder.java,v 1.6 2004/05/23 20:05:25 spullara Exp $
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.security.AccessController;
053    import java.security.PrivilegedActionException;
054    import java.security.PrivilegedExceptionAction;
055    import java.util.Iterator;
056    import java.util.Map;
057    
058    import javax.xml.parsers.DocumentBuilder;
059    import javax.xml.parsers.DocumentBuilderFactory;
060    import javax.xml.parsers.FactoryConfigurationError;
061    import javax.xml.parsers.ParserConfigurationException;
062    
063    import org.w3c.dom.Document;
064    import org.w3c.dom.Element;
065    import org.w3c.dom.Node;
066    import org.xml.sax.InputSource;
067    import org.xml.sax.SAXException;
068    
069    /**
070     * A helper class for creating a W3C DOM tree
071     * 
072     * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
073     * @version $Revision: 1.6 $
074     */
075    public class DOMBuilder extends BuilderSupport {
076    
077        Document document;
078        DocumentBuilder documentBuilder;
079    
080        public static DOMBuilder newInstance() throws ParserConfigurationException, FactoryConfigurationError {
081            DocumentBuilder documentBuilder = null;
082            try {
083                            documentBuilder = (DocumentBuilder) AccessController.doPrivileged(new PrivilegedExceptionAction() {
084                                    public Object run() throws ParserConfigurationException {
085                                            return DocumentBuilderFactory.newInstance().newDocumentBuilder();
086                                    }
087                            });
088            } catch (PrivilegedActionException pae) {
089                    Exception e = pae.getException();
090                    if (e instanceof ParserConfigurationException) {
091                            throw (ParserConfigurationException) e;
092                    } else {
093                            throw new RuntimeException(e);
094                    }
095            }
096            return new DOMBuilder(documentBuilder);
097        }
098    
099        public static Document parse(Reader reader) throws SAXException, IOException, ParserConfigurationException {
100                    DocumentBuilder documentBuilder = null;
101                    try {
102                                    documentBuilder = (DocumentBuilder) AccessController.doPrivileged(new PrivilegedExceptionAction() {
103                                            public Object run() throws ParserConfigurationException {
104                                                    return DocumentBuilderFactory.newInstance().newDocumentBuilder();
105                                            }
106                                    });
107                    } catch (PrivilegedActionException pae) {
108                            Exception e = pae.getException();
109                            if (e instanceof ParserConfigurationException) {
110                                    throw (ParserConfigurationException) e;
111                            } else {
112                                    throw new RuntimeException(e);
113                            }
114                    }
115                    return documentBuilder.parse(new InputSource(reader));
116        }
117        
118        public DOMBuilder(Document document) {
119            this.document = document;
120        }
121    
122        public DOMBuilder(DocumentBuilder documentBuilder) {
123            this.documentBuilder = documentBuilder;
124        }
125    
126        protected void setParent(Object parent, Object child) {
127            Node current = (Node) parent;
128            Node node = (Node) child;
129    
130            current.appendChild(node);
131        }
132    
133        protected Object createNode(Object name) {
134            if (document == null) {
135                document = createDocument();
136            }
137            if (name instanceof QName) {
138                QName qname = (QName) name;
139                return document.createElementNS(qname.getNamespaceURI(), qname.getQualifiedName());
140            }
141            else {
142                return document.createElement(name.toString());
143            }
144        }
145    
146        protected Document createDocument() {
147            if (documentBuilder == null) {
148                throw new IllegalArgumentException("No Document or DOMImplementation available so cannot create Document");
149            }
150            else {
151                return documentBuilder.newDocument();
152            }
153        }
154    
155        protected Object createNode(Object name, Object value) {
156            Element element = (Element) createNode(name);
157            element.appendChild(document.createTextNode(value.toString()));
158            return element;
159        }
160    
161        protected Object createNode(Object name, Map attributes, Object value) {
162            Element element = (Element) createNode(name, attributes);
163            element.appendChild(document.createTextNode(value.toString()));
164            return element;
165        }
166        
167        protected Object createNode(Object name, Map attributes) {
168            Element element = (Element) createNode(name);
169            for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
170                Map.Entry entry = (Map.Entry) iter.next();
171                String attrName = entry.getKey().toString();
172                Object value = entry.getValue();
173                if ("xmlns".equals(attrName)) {
174                    if (value instanceof Map) {
175                        appendNamespaceAttributes(element, (Map) value);
176                    }
177                    else {
178                        throw new IllegalArgumentException("The value of the xmlns attribute must be a Map of QNames to String URIs");
179                    }
180                }
181                else {
182                    element.setAttribute(attrName, value.toString());
183                }
184            }
185            return element;
186        }
187    
188        protected void appendNamespaceAttributes(Element element, Map attributes) {
189            for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
190                Map.Entry entry = (Map.Entry) iter.next();
191                Object key = entry.getKey();
192                Object value = entry.getValue();
193                if (value == null) {
194                    throw new IllegalArgumentException("The value of key: " + key + " cannot be null");
195                }
196                if (key instanceof String) {
197                    String prefix = (String) key;
198                    
199                    //System.out.println("Creating namespace for prefix: " + prefix + " with value: " + value);
200                    
201                    //element.setAttributeNS("http://www.w3.org/XML/1998/namespace", "xmlns:" + prefix, value.toString());
202                    element.setAttributeNS("", prefix, value.toString());
203                }
204                else if (key instanceof QName) {
205                    QName qname = (QName) key;
206                    element.setAttributeNS(qname.getNamespaceURI(), qname.getQualifiedName(), value.toString());
207                }
208                else {
209                    throw new IllegalArgumentException("The key: " + key + " should be an instanceof of " + QName.class);
210                }
211            }
212        }
213    }