001    /*
002     $Id: XmlNodePrinter.java,v 1.1 2005/07/13 18:54:45 cstein 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    
047    package groovy.util;
048    
049    import groovy.xml.QName;
050    
051    import java.io.OutputStreamWriter;
052    import java.io.PrintWriter;
053    import java.util.Iterator;
054    import java.util.List;
055    import java.util.Map;
056    
057    import org.codehaus.groovy.runtime.InvokerHelper;
058    
059    /**
060     * Prints a node with all childs in XML format.
061     * 
062     * @see groovy.util.NodePrinter
063     * @author Christian Stein
064     */
065    public class XmlNodePrinter {
066    
067        protected final IndentPrinter out;
068        private final String quote;
069    
070        public XmlNodePrinter() {
071            this(new PrintWriter(new OutputStreamWriter(System.out)));
072        }
073    
074        public XmlNodePrinter(PrintWriter out) {
075            this(out, "  ");
076        }
077    
078        public XmlNodePrinter(PrintWriter out, String indent) {
079            this(out, indent, "\"");
080        }
081    
082        public XmlNodePrinter(PrintWriter out, String indent, String quote) {
083            this(new IndentPrinter(out, indent), quote);
084        }
085    
086        public XmlNodePrinter(IndentPrinter out, String quote) {
087            if (out == null) {
088                throw new IllegalArgumentException("Argument 'IndentPrinter out' must not be null!");
089            }
090            this.out = out;
091            this.quote = quote;
092        }
093    
094        public String getNameOfNode(Node node) {
095            if (node == null) {
096                throw new IllegalArgumentException("Node must not be null!");
097            }
098            Object name = node.name();
099            if (name instanceof QName) {
100                QName qname = (QName) name;
101                return /* qname.getPrefix() + ":" + */qname.getLocalPart();
102            }
103            return name.toString();
104        }
105    
106        public boolean isEmptyElement(Node node) {
107            if (node == null) {
108                throw new IllegalArgumentException("Node must not be null!");
109            }
110            if (!node.children().isEmpty()) {
111                return false;
112            }
113            String text = node.text();
114            if (text.length() > 0) {
115                return false;
116            }
117            return true;
118        }
119    
120        public void print(Node node) {
121            /*
122             * Handle empty elements like '<br/>', '<img/> or '<hr noshade="noshade"/>.
123             */
124            if (isEmptyElement(node)) {
125                // System.err.println("empty-dead");
126                printLineBegin();
127                out.print("<");
128                out.print(getNameOfNode(node));
129                printNameAttributes(node.attributes());
130                out.print("/>");
131                printLineEnd(); // "node named '" + node.name() + "'"
132                out.flush();
133                return;
134            }
135    
136            /*
137             * Handle GSP tag element!
138             */
139            if (printSpecialNode(node)) {
140                // System.err.println("special-dead");
141                out.flush();
142                return;
143            }
144    
145            /*
146             * Handle normal element like <html> ... </html>.
147             */
148            Object value = node.value();
149            if (value instanceof List) {
150                printName(node, true);
151                printList((List) value);
152                printName(node, false);
153                out.flush();
154                return;
155            }
156    
157            /*
158             * Still here?!
159             */
160            throw new RuntimeException("Unsupported node value: " + node.value());
161        }
162    
163        protected void printLineBegin() {
164            out.printIndent();
165        }
166    
167        protected void printLineEnd() {
168            printLineEnd(null);
169        }
170    
171        protected void printLineEnd(String comment) {
172            if (comment != null) {
173                out.print(" <!-- ");
174                out.print(comment);
175                out.print(" -->");
176            }
177            out.print("\n");
178        }
179    
180        protected void printList(List list) {
181            out.incrementIndent();
182            for (Iterator iter = list.iterator(); iter.hasNext();) {
183                Object value = iter.next();
184                /*
185                 * If the current value is a node, recurse into that node.
186                 */
187                if (value instanceof Node) {
188                    print((Node) value);
189                    continue;
190                }
191                /*
192                 * Print out "simple" text nodes.
193                 */
194                printLineBegin();
195                out.print(InvokerHelper.toString(value));
196                printLineEnd();
197            }
198            out.decrementIndent();
199        }
200    
201        protected void printName(Node node, boolean begin) {
202            if (node == null) {
203                throw new NullPointerException("Node must not be null.");
204            }
205            Object name = node.name();
206            if (name == null) {
207                throw new NullPointerException("Name must not be null.");
208            }
209            printLineBegin();
210            out.print("<");
211            if (!begin) {
212                out.print("/");
213            }
214            out.print(getNameOfNode(node));
215            if (begin) {
216                printNameAttributes(node.attributes());
217            }
218            out.print(">");
219            printLineEnd();
220        }
221    
222        protected void printNameAttributes(Map attributes) {
223            if (attributes == null || attributes.isEmpty()) {
224                return;
225            }
226            out.print(" ");
227            boolean first = true;
228            for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
229                Map.Entry entry = (Map.Entry) iter.next();
230                if (first) {
231                    first = false;
232                } else {
233                    out.print(" ");
234                }
235                out.print(entry.getKey().toString());
236                out.print("=");
237                Object value = entry.getValue();
238                if (value instanceof String) {
239                    out.print(quote);
240                    out.print((String) value);
241                    out.print(quote);
242                    continue;
243                }
244                out.print(InvokerHelper.toString(value));
245            }
246        }
247    
248        protected boolean printSpecialNode(Node node) {
249            return false;
250        }
251    
252    }