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, 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(hasAttributes ? ")" : "()", endWithComma); 125 } else { 126 Node node = list.item(0); 127 if (length == 1 && node instanceof Text) { 128 Text textNode = (Text) node; 129 String text = getTextNodeData(textNode); 130 if (hasAttributes) print(", '"); 131 else print("('"); 132 print(text); 133 printEnd("')", endWithComma); 134 } else if (mixedContent(list)) { 135 println(" ["); 136 out.incrementIndent(); 137 for (node = element.getFirstChild(); node != null; node = node.getNextSibling()) { 138 boolean useComma = node.getNextSibling() != null; 139 print(node, namespaces, useComma); 140 } 141 out.decrementIndent(); 142 printIndent(); 143 printEnd("]", endWithComma); 144 } else { 145 println(") {"); 146 out.incrementIndent(); 147 printChildren(element, namespaces); 148 out.decrementIndent(); 149 printIndent(); 150 printEnd("}", endWithComma); 151 } 152 } 153 } 154 155 protected void printPI(ProcessingInstruction instruction, boolean endWithComma) { 156 printIndent(); 157 print("xml.pi('"); 158 print(instruction.getTarget()); 159 print("', '"); 160 print(instruction.getData()); 161 printEnd("');", endWithComma); 162 } 163 164 protected void printComment(Comment comment, boolean endWithComma) { 165 String text = comment.getData().trim(); 166 if (text.length() >0) { 167 printIndent(); 168 print("/* "); 169 print(text); 170 printEnd(" */", endWithComma); 171 } 172 } 173 174 protected void printText(Text node, boolean endWithComma) { 175 String text = getTextNodeData(node); 176 if (text.length() > 0) { 177 printIndent(); 178 // print("xml.append('"); 179 // print(text); 180 // println("');"); 181 print("'"); 182 print(text); 183 printEnd("'", endWithComma); 184 } 185 } 186 187 protected Map defineNamespaces(Element element, Map namespaces) { 188 Map answer = null; 189 String prefix = element.getPrefix(); 190 if (prefix != null && prefix.length() > 0 && !namespaces.containsKey(prefix)) { 191 answer = new HashMap(namespaces); 192 defineNamespace(answer, prefix, element.getNamespaceURI()); 193 } 194 NamedNodeMap attributes = element.getAttributes(); 195 int length = attributes.getLength(); 196 for (int i = 0; i < length; i++) { 197 Attr attribute = (Attr) attributes.item(i); 198 prefix = attribute.getPrefix(); 199 if (prefix != null && prefix.length() > 0 && !namespaces.containsKey(prefix)) { 200 if (answer == null) { 201 answer = new HashMap(namespaces); 202 } 203 defineNamespace(answer, prefix, attribute.getNamespaceURI()); 204 } 205 } 206 return (answer != null) ? answer : namespaces; 207 } 208 209 protected void defineNamespace(Map namespaces, String prefix, String uri) { 210 namespaces.put(prefix, uri); 211 if (!prefix.equals("xmlns") && !prefix.equals("xml")) { 212 printIndent(); 213 print(prefix); 214 print(" = xmlns.namespace('"); 215 print(uri); 216 println("')"); 217 } 218 } 219 220 protected boolean printAttributes(Element element) { 221 boolean hasAttribute = false; 222 NamedNodeMap attributes = element.getAttributes(); 223 int length = attributes.getLength(); 224 if (length > 0) { 225 StringBuffer buffer = new StringBuffer(); 226 for (int i = 0; i < length; i++) { 227 printAttributeWithPrefix((Attr) attributes.item(i), buffer); 228 } 229 print("("); 230 for (int i = 0; i < length; i++) { 231 hasAttribute = printAttributeWithoutPrefix((Attr) attributes.item(i), hasAttribute); 232 } 233 if (buffer.length() > 0) { 234 if (hasAttribute) { 235 print(", "); 236 } 237 print("xmlns=["); 238 print(buffer.toString()); 239 print("]"); 240 hasAttribute = true; 241 } 242 } 243 return hasAttribute; 244 } 245 246 private void printAttributeWithPrefix(Attr attribute, StringBuffer buffer) { 247 String prefix = attribute.getPrefix(); 248 if (prefix != null && prefix.length() > 0) { 249 if (buffer.length() > 0) { 250 buffer.append(", "); 251 } 252 buffer.append(prefix); 253 buffer.append("."); 254 buffer.append(getLocalName(attribute)); 255 buffer.append(":'"); 256 buffer.append(getAttributeValue(attribute)); 257 buffer.append("'"); 258 } 259 } 260 261 private String getAttributeValue(Attr attribute) { 262 return attribute.getValue(); 263 } 264 265 private boolean printAttributeWithoutPrefix(Attr attribute, boolean hasAttribute) { 266 String prefix = attribute.getPrefix(); 267 if (prefix == null || prefix.length() == 0) { 268 if (!hasAttribute) { 269 hasAttribute = true; 270 } else { 271 print(", "); 272 } 273 print(getLocalName(attribute)); 274 print(":'"); 275 print(getAttributeValue(attribute)); 276 print("'"); 277 } 278 return hasAttribute; 279 } 280 281 protected String getTextNodeData(Text node) { 282 return node.getData().trim(); 283 } 284 285 protected boolean mixedContent(NodeList list) { 286 boolean hasText = false; 287 boolean hasElement = false; 288 for (int i = 0, size = list.getLength(); i < size; i++) { 289 Node node = list.item(i); 290 if (node instanceof Element) { 291 hasElement = true; 292 } else if (node instanceof Text) { 293 String text = getTextNodeData((Text) node); 294 if (text.length() > 0) { 295 hasText = true; 296 } 297 } 298 } 299 return hasText && hasElement; 300 } 301 302 protected void printChildren(Node parent, Map namespaces) { 303 for (Node node = parent.getFirstChild(); node != null; node = node.getNextSibling()) { 304 print(node, namespaces, false); 305 } 306 } 307 308 protected String getLocalName(Node node) { 309 String answer = node.getLocalName(); 310 if (answer == null) { 311 answer = node.getNodeName(); 312 } 313 return answer.trim(); 314 } 315 316 protected void printEnd(String text, boolean endWithComma) { 317 if (endWithComma) { 318 print(text); 319 println(","); 320 } else { 321 println(text); 322 } 323 } 324 325 protected void println(String text) { 326 out.println(text); 327 } 328 329 protected void print(String text) { 330 out.print(text); 331 } 332 333 protected void printIndent() { 334 out.printIndent(); 335 } 336 }