001 /*
002 $Id: DomToGroovy.java,v 1.5 2003/11/04 12:00:48 jstrachan 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 org.codehaus.groovy.tools.xml;
047
048 import groovy.util.IndentPrinter;
049
050 import java.io.PrintWriter;
051 import java.util.HashMap;
052 import java.util.Map;
053
054 import org.w3c.dom.Attr;
055 import org.w3c.dom.Comment;
056 import org.w3c.dom.Document;
057 import org.w3c.dom.Element;
058 import org.w3c.dom.NamedNodeMap;
059 import org.w3c.dom.Node;
060 import org.w3c.dom.NodeList;
061 import org.w3c.dom.ProcessingInstruction;
062 import org.w3c.dom.Text;
063
064 /**
065 * A SAX handler for turning XML into Groovy scripts
066 *
067 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
068 * @version $Revision: 1.5 $
069 */
070 public class DomToGroovy {
071
072 private IndentPrinter out;
073
074 public DomToGroovy(PrintWriter out) {
075 this(new IndentPrinter(out));
076 }
077
078 public DomToGroovy(IndentPrinter out) {
079 this.out = out;
080 }
081
082
083 public void print(Document document) {
084 printChildren(document, new HashMap());
085 }
086
087 // Implementation methods
088 //-------------------------------------------------------------------------
089 protected void print(Node node, Map namespaces, boolean endWithComma) {
090 switch (node.getNodeType()) {
091 case Node.ELEMENT_NODE :
092 printElement((Element) node, namespaces, endWithComma);
093 break;
094 case Node.PROCESSING_INSTRUCTION_NODE :
095 printPI((ProcessingInstruction) node, endWithComma);
096 break;
097 case Node.TEXT_NODE :
098 printText((Text) node, endWithComma);
099 break;
100 case Node.COMMENT_NODE :
101 printComment((Comment) node, endWithComma);
102 break;
103 }
104 }
105
106 protected void printElement(Element element, Map namespaces, boolean endWithComma) {
107 namespaces = defineNamespaces(element, namespaces);
108
109 element.normalize();
110 printIndent();
111
112 String prefix = element.getPrefix();
113 if (prefix != null && prefix.length() > 0) {
114 print(prefix);
115 print(".");
116 }
117 print(getLocalName(element));
118
119 boolean hasAttributes = printAttributes(element);
120
121 NodeList list = element.getChildNodes();
122 int length = list.getLength();
123 if (length == 0) {
124 printEnd("", endWithComma);
125 }
126 else {
127 Node node = list.item(0);
128 if (length == 1 && node instanceof Text) {
129 Text textNode = (Text) node;
130 String text = getTextNodeData(textNode);
131 if (hasAttributes) {
132 print(" [\"");
133 print(text);
134 printEnd("\"]", endWithComma);
135 }
136 else {
137 print("(\"");
138 print(text);
139 printEnd("\")", endWithComma);
140 }
141 }
142 else if (mixedContent(list)) {
143 println(" [");
144 out.incrementIndent();
145 for (node = element.getFirstChild(); node != null; node = node.getNextSibling()) {
146 boolean useComma = node.getNextSibling() != null;
147 print(node, namespaces, useComma);
148 }
149 out.decrementIndent();
150 printIndent();
151 printEnd("]", endWithComma);
152 }
153 else {
154 println(" {");
155 out.incrementIndent();
156 printChildren(element, namespaces);
157 out.decrementIndent();
158 printIndent();
159 printEnd("}", endWithComma);
160 }
161 }
162 }
163
164 protected void printPI(ProcessingInstruction instruction, boolean endWithComma) {
165 printIndent();
166 print("xml.pi('");
167 print(instruction.getTarget());
168 print("', '");
169 print(instruction.getData());
170 printEnd("');", endWithComma);
171 }
172
173 protected void printComment(Comment comment, boolean endWithComma) {
174 String text = comment.getData().trim();
175 if (text.length() >0) {
176 printIndent();
177 print("/* ");
178 print(text);
179 printEnd(" */", endWithComma);
180 }
181 }
182
183 protected void printText(Text node, boolean endWithComma) {
184 String text = getTextNodeData(node);
185 if (text.length() > 0) {
186 printIndent();
187 // print("xml.append('");
188 // print(text);
189 // println("');");
190 print("\"");
191 print(text);
192 printEnd("\"", endWithComma);
193 }
194 }
195
196 protected Map defineNamespaces(Element element, Map namespaces) {
197 Map answer = null;
198 String prefix = element.getPrefix();
199 if (prefix != null && prefix.length() > 0 && !namespaces.containsKey(prefix)) {
200 answer = new HashMap(namespaces);
201 defineNamespace(answer, prefix, element.getNamespaceURI());
202 }
203 NamedNodeMap attributes = element.getAttributes();
204 int length = attributes.getLength();
205 for (int i = 0; i < length; i++) {
206 Attr attribute = (Attr) attributes.item(i);
207 prefix = attribute.getPrefix();
208 if (prefix != null && prefix.length() > 0 && !namespaces.containsKey(prefix)) {
209 if (answer == null) {
210 answer = new HashMap(namespaces);
211 }
212 defineNamespace(answer, prefix, attribute.getNamespaceURI());
213 }
214 }
215 return (answer != null) ? answer : namespaces;
216 }
217
218 protected void defineNamespace(Map namespaces, String prefix, String uri) {
219 namespaces.put(prefix, uri);
220 if (!prefix.equals("xmlns") && !prefix.equals("xml")) {
221 printIndent();
222 print(prefix);
223 print(" = xmlns.namespace('");
224 print(uri);
225 println("')");
226 }
227 }
228
229 protected boolean printAttributes(Element element) {
230 boolean hasAttribute = false;
231
232 NamedNodeMap attributes = element.getAttributes();
233 int length = attributes.getLength();
234 if (length > 0) {
235 StringBuffer buffer = new StringBuffer();
236 for (int i = 0; i < length; i++) {
237 Attr attribute = (Attr) attributes.item(i);
238 String prefix = attribute.getPrefix();
239 if (prefix != null && prefix.length() > 0) {
240 if (buffer.length() > 0) {
241 buffer.append(", ");
242 }
243 buffer.append(prefix);
244 buffer.append(".");
245 buffer.append(getLocalName(attribute));
246 buffer.append(":'");
247 buffer.append(attribute.getValue());
248 buffer.append("'");
249 }
250 }
251
252 print("(");
253 for (int i = 0; i < length; i++) {
254 Attr attribute = (Attr) attributes.item(i);
255 String prefix = attribute.getPrefix();
256 if (prefix == null || prefix.length() == 0) {
257 if (!hasAttribute) {
258 hasAttribute = true;
259 }
260 else {
261 print(", ");
262 }
263 print(getLocalName(attribute));
264 print(":'");
265 print(attribute.getValue());
266 print("'");
267 }
268 }
269 if (buffer.length() > 0) {
270 if (hasAttribute) {
271 print(", ");
272 }
273 print("xmlns=[");
274 print(buffer.toString());
275 print("]");
276 hasAttribute = true;
277 }
278 print(")");
279 }
280 return hasAttribute;
281 }
282
283 protected String getTextNodeData(Text node) {
284 String text = node.getData().trim();
285 return text;
286 }
287
288 protected boolean mixedContent(NodeList list) {
289 boolean hasText = false;
290 boolean hasElement = false;
291 for (int i = 0, size = list.getLength(); i < size; i++) {
292 Node node = list.item(i);
293 if (node instanceof Element) {
294 hasElement = true;
295 }
296 else if (node instanceof Text) {
297 String text = getTextNodeData((Text) node);
298 if (text.length() > 0) {
299 hasText = true;
300 }
301 }
302 }
303 return hasText && hasElement;
304 }
305
306 protected void printChildren(Node parent, Map namespaces) {
307 for (Node node = parent.getFirstChild(); node != null; node = node.getNextSibling()) {
308 print(node, namespaces, false);
309 }
310 }
311
312 protected String getLocalName(Node node) {
313 String answer = node.getLocalName();
314 if (answer == null) {
315 answer = node.getNodeName();
316 }
317 return answer.trim();
318 }
319
320 protected void printEnd(String text, boolean endWithComma) {
321 if (endWithComma) {
322 print(text);
323 println(",");
324 }
325 else {
326 println(text);
327 }
328 }
329
330 protected void println(String text) {
331 out.println(text); }
332
333 protected void print(String text) {
334 out.print(text);
335 }
336
337 protected void printIndent() {
338 out.printIndent();
339 }
340 }