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 }