001: /*
002: $Id: DomToGroovy.java 4530 2006-12-21 11:51:22Z 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 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 James Strachan
068: * @author paulk
069: */
070: public class DomToGroovy {
071:
072: private IndentPrinter out;
073:
074: public DomToGroovy(PrintWriter out) {
075: this (new IndentPrinter(out));
076: }
077:
078: // TODO allow string quoting delimiter to be specified, e.g. ' vs "
079: public DomToGroovy(IndentPrinter out) {
080: this .out = out;
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,
107: boolean endWithComma) {
108: namespaces = defineNamespaces(element, namespaces);
109:
110: element.normalize();
111: printIndent();
112:
113: String prefix = element.getPrefix();
114: if (prefix != null && prefix.length() > 0) {
115: print(prefix);
116: print(".");
117: }
118: print(getLocalName(element));
119:
120: boolean hasAttributes = printAttributes(element);
121:
122: NodeList list = element.getChildNodes();
123: int length = list.getLength();
124: if (length == 0) {
125: printEnd(hasAttributes ? ")" : "()", endWithComma);
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: else
134: print("('");
135: print(text);
136: printEnd("')", endWithComma);
137: } else if (mixedContent(list)) {
138: println(" [");
139: out.incrementIndent();
140: for (node = element.getFirstChild(); node != null; node = node
141: .getNextSibling()) {
142: boolean useComma = node.getNextSibling() != null;
143: print(node, namespaces, useComma);
144: }
145: out.decrementIndent();
146: printIndent();
147: printEnd("]", endWithComma);
148: } else {
149: println(") {");
150: out.incrementIndent();
151: printChildren(element, namespaces);
152: out.decrementIndent();
153: printIndent();
154: printEnd("}", endWithComma);
155: }
156: }
157: }
158:
159: protected void printPI(ProcessingInstruction instruction,
160: boolean endWithComma) {
161: printIndent();
162: print("xml.pi('");
163: print(instruction.getTarget());
164: print("', '");
165: print(instruction.getData());
166: printEnd("');", endWithComma);
167: }
168:
169: protected void printComment(Comment comment, boolean endWithComma) {
170: String text = comment.getData().trim();
171: if (text.length() > 0) {
172: printIndent();
173: print("/* ");
174: print(text);
175: printEnd(" */", endWithComma);
176: }
177: }
178:
179: protected void printText(Text node, boolean endWithComma) {
180: String text = getTextNodeData(node);
181: if (text.length() > 0) {
182: printIndent();
183: // print("xml.append('");
184: // print(text);
185: // println("');");
186: print("'");
187: print(text);
188: printEnd("'", endWithComma);
189: }
190: }
191:
192: protected Map defineNamespaces(Element element, Map namespaces) {
193: Map answer = null;
194: String prefix = element.getPrefix();
195: if (prefix != null && prefix.length() > 0
196: && !namespaces.containsKey(prefix)) {
197: answer = new HashMap(namespaces);
198: defineNamespace(answer, prefix, element.getNamespaceURI());
199: }
200: NamedNodeMap attributes = element.getAttributes();
201: int length = attributes.getLength();
202: for (int i = 0; i < length; i++) {
203: Attr attribute = (Attr) attributes.item(i);
204: prefix = attribute.getPrefix();
205: if (prefix != null && prefix.length() > 0
206: && !namespaces.containsKey(prefix)) {
207: if (answer == null) {
208: answer = new HashMap(namespaces);
209: }
210: defineNamespace(answer, prefix, attribute
211: .getNamespaceURI());
212: }
213: }
214: return (answer != null) ? answer : namespaces;
215: }
216:
217: protected void defineNamespace(Map namespaces, String prefix,
218: 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: NamedNodeMap attributes = element.getAttributes();
232: int length = attributes.getLength();
233: if (length > 0) {
234: StringBuffer buffer = new StringBuffer();
235: for (int i = 0; i < length; i++) {
236: printAttributeWithPrefix((Attr) attributes.item(i),
237: buffer);
238: }
239: print("(");
240: for (int i = 0; i < length; i++) {
241: hasAttribute = printAttributeWithoutPrefix(
242: (Attr) attributes.item(i), hasAttribute);
243: }
244: if (buffer.length() > 0) {
245: if (hasAttribute) {
246: print(", ");
247: }
248: print("xmlns=[");
249: print(buffer.toString());
250: print("]");
251: hasAttribute = true;
252: }
253: }
254: return hasAttribute;
255: }
256:
257: private void printAttributeWithPrefix(Attr attribute,
258: StringBuffer buffer) {
259: String prefix = attribute.getPrefix();
260: if (prefix != null && prefix.length() > 0) {
261: if (buffer.length() > 0) {
262: buffer.append(", ");
263: }
264: buffer.append(prefix);
265: buffer.append(".");
266: buffer.append(getLocalName(attribute));
267: buffer.append(":'");
268: buffer.append(getAttributeValue(attribute));
269: buffer.append("'");
270: }
271: }
272:
273: private String getAttributeValue(Attr attribute) {
274: return attribute.getValue();
275: }
276:
277: private boolean printAttributeWithoutPrefix(Attr attribute,
278: boolean hasAttribute) {
279: String prefix = attribute.getPrefix();
280: if (prefix == null || prefix.length() == 0) {
281: if (!hasAttribute) {
282: hasAttribute = true;
283: } else {
284: print(", ");
285: }
286: print(getLocalName(attribute));
287: print(":'");
288: print(getAttributeValue(attribute));
289: print("'");
290: }
291: return hasAttribute;
292: }
293:
294: protected String getTextNodeData(Text node) {
295: return node.getData().trim();
296: }
297:
298: protected boolean mixedContent(NodeList list) {
299: boolean hasText = false;
300: boolean hasElement = false;
301: for (int i = 0, size = list.getLength(); i < size; i++) {
302: Node node = list.item(i);
303: if (node instanceof Element) {
304: hasElement = true;
305: } else if (node instanceof Text) {
306: String text = getTextNodeData((Text) node);
307: if (text.length() > 0) {
308: hasText = true;
309: }
310: }
311: }
312: return hasText && hasElement;
313: }
314:
315: protected void printChildren(Node parent, Map namespaces) {
316: for (Node node = parent.getFirstChild(); node != null; node = node
317: .getNextSibling()) {
318: print(node, namespaces, false);
319: }
320: }
321:
322: protected String getLocalName(Node node) {
323: String answer = node.getLocalName();
324: if (answer == null) {
325: answer = node.getNodeName();
326: }
327: return answer.trim();
328: }
329:
330: protected void printEnd(String text, boolean endWithComma) {
331: if (endWithComma) {
332: print(text);
333: println(",");
334: } else {
335: println(text);
336: }
337: }
338:
339: protected void println(String text) {
340: out.println(text);
341: }
342:
343: protected void print(String text) {
344: out.print(text);
345: }
346:
347: protected void printIndent() {
348: out.printIndent();
349: }
350: }
|