| /* |
| * Copyright 2003-2007 the original author or authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.codehaus.groovy.tools.xml; |
| |
| import groovy.util.IndentPrinter; |
| import org.w3c.dom.*; |
| |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import java.io.File; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * A SAX handler for turning XML into Groovy scripts |
| * |
| * @author James Strachan |
| * @author paulk |
| */ |
| public class DomToGroovy { |
| |
| private IndentPrinter out; |
| private boolean inMixed = false; |
| private String qt = "'"; |
| private List keywords = Arrays.asList(new String[]{ |
| "import", "private", "public", "protected" |
| }); |
| |
| public DomToGroovy(PrintWriter out) { |
| this(new IndentPrinter(out)); |
| } |
| |
| // TODO allow string quoting delimiter to be specified, e.g. ' vs " |
| public DomToGroovy(IndentPrinter out) { |
| this.out = out; |
| } |
| |
| public void print(Document document) { |
| printChildren(document, new HashMap()); |
| } |
| |
| public static void main(String[] args) { |
| if (args.length < 1) { |
| System.out.println("Usage: DomToGroovy infilename [outfilename]"); |
| System.exit(1); |
| } |
| Document document = null; |
| try { |
| document = parse(args[0]); |
| } catch (Exception e) { |
| System.out.println("Unable to parse input file '" + args[0] + "': " + e.getMessage()); |
| System.exit(1); |
| } |
| PrintWriter writer = null; |
| if (args.length < 2) { |
| writer = new PrintWriter(System.out); |
| } else { |
| try { |
| writer = new PrintWriter(new FileWriter(new File(args[1]))); |
| } catch (IOException e) { |
| System.out.println("Unable to create output file '" + args[1] + "': " + e.getMessage()); |
| System.exit(1); |
| } |
| } |
| DomToGroovy converter = new DomToGroovy(writer); |
| converter.out.incrementIndent(); |
| writer.println("#!/bin/groovy"); |
| writer.println(); |
| writer.println("// generated from " + args[0]); |
| writer.println("System.out << new groovy.xml.StreamingMarkupBuilder().bind {"); |
| converter.print(document); |
| writer.println("}"); |
| writer.close(); |
| } |
| |
| // Implementation methods |
| //------------------------------------------------------------------------- |
| private static Document parse(String name) throws Exception { |
| DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); |
| factory.setNamespaceAware(true); |
| DocumentBuilder builder = factory.newDocumentBuilder(); |
| return builder.parse(new File(name)); |
| } |
| |
| protected void print(Node node, Map namespaces, boolean endWithComma) { |
| switch (node.getNodeType()) { |
| case Node.ELEMENT_NODE : |
| printElement((Element) node, namespaces, endWithComma); |
| break; |
| case Node.PROCESSING_INSTRUCTION_NODE : |
| printPI((ProcessingInstruction) node, endWithComma); |
| break; |
| case Node.TEXT_NODE : |
| printText((Text) node, endWithComma); |
| break; |
| case Node.COMMENT_NODE : |
| printComment((Comment) node, endWithComma); |
| break; |
| } |
| } |
| |
| protected void printElement(Element element, Map namespaces, boolean endWithComma) { |
| namespaces = defineNamespaces(element, namespaces); |
| |
| element.normalize(); |
| printIndent(); |
| |
| String prefix = element.getPrefix(); |
| boolean hasPrefix = prefix != null && prefix.length() > 0; |
| String localName = getLocalName(element); |
| boolean isKeyword = checkEscaping(localName); |
| if (isKeyword || hasPrefix) print(qt); |
| if (hasPrefix) { |
| print(prefix); |
| print("."); |
| } |
| print(localName); |
| if (isKeyword || hasPrefix) print(qt); |
| print("("); |
| |
| boolean hasAttributes = printAttributes(element); |
| |
| NodeList list = element.getChildNodes(); |
| int length = list.getLength(); |
| if (length == 0) { |
| printEnd(")", endWithComma); |
| } else { |
| Node node = list.item(0); |
| if (length == 1 && node instanceof Text) { |
| Text textNode = (Text) node; |
| String text = getTextNodeData(textNode); |
| if (hasAttributes) print(", "); |
| printQuoted(text); |
| printEnd(")", endWithComma); |
| } else if (mixedContent(list)) { |
| println(") {"); |
| out.incrementIndent(); |
| boolean oldInMixed = inMixed; |
| inMixed = true; |
| for (node = element.getFirstChild(); node != null; node = node.getNextSibling()) { |
| print(node, namespaces, false); |
| } |
| inMixed = oldInMixed; |
| out.decrementIndent(); |
| printIndent(); |
| printEnd("}", endWithComma); |
| } else { |
| println(") {"); |
| out.incrementIndent(); |
| printChildren(element, namespaces); |
| out.decrementIndent(); |
| printIndent(); |
| printEnd("}", endWithComma); |
| } |
| } |
| } |
| |
| private void printQuoted(String text) { |
| if (text.indexOf("\n") != -1) { |
| print("'''"); |
| print(text); |
| print("'''"); |
| } else { |
| print(qt); |
| print(escapeQuote(text)); |
| print(qt); |
| } |
| } |
| |
| protected void printPI(ProcessingInstruction instruction, boolean endWithComma) { |
| printIndent(); |
| print("mkp.pi(" + qt); |
| print(instruction.getTarget()); |
| print(qt + ", " + qt); |
| print(instruction.getData()); |
| printEnd(qt + ");", endWithComma); |
| } |
| |
| protected void printComment(Comment comment, boolean endWithComma) { |
| String text = comment.getData().trim(); |
| if (text.length() >0) { |
| printIndent(); |
| print("/* "); |
| print(text); |
| printEnd(" */", endWithComma); |
| } |
| } |
| |
| protected void printText(Text node, boolean endWithComma) { |
| String text = getTextNodeData(node); |
| if (text.length() > 0) { |
| printIndent(); |
| if (inMixed) print("mkp.yield "); |
| printQuoted(text); |
| printEnd("", endWithComma); |
| } |
| } |
| |
| private String escapeQuote(String text) { |
| return text.replaceAll("\\\\", "\\\\\\\\").replaceAll(qt, "\\\\" + qt); |
| } |
| |
| protected Map defineNamespaces(Element element, Map namespaces) { |
| Map answer = null; |
| String prefix = element.getPrefix(); |
| if (prefix != null && prefix.length() > 0 && !namespaces.containsKey(prefix)) { |
| answer = new HashMap(namespaces); |
| defineNamespace(answer, prefix, element.getNamespaceURI()); |
| } |
| NamedNodeMap attributes = element.getAttributes(); |
| int length = attributes.getLength(); |
| for (int i = 0; i < length; i++) { |
| Attr attribute = (Attr) attributes.item(i); |
| prefix = attribute.getPrefix(); |
| if (prefix != null && prefix.length() > 0 && !namespaces.containsKey(prefix)) { |
| if (answer == null) { |
| answer = new HashMap(namespaces); |
| } |
| defineNamespace(answer, prefix, attribute.getNamespaceURI()); |
| } |
| } |
| return (answer != null) ? answer : namespaces; |
| } |
| |
| protected void defineNamespace(Map namespaces, String prefix, String uri) { |
| namespaces.put(prefix, uri); |
| if (!prefix.equals("xmlns") && !prefix.equals("xml")) { |
| printIndent(); |
| print("mkp.declareNamespace("); |
| print(prefix); |
| print(":" + qt); |
| print(uri); |
| println(qt + ")"); |
| } |
| } |
| |
| protected boolean printAttributes(Element element) { |
| boolean hasAttribute = false; |
| NamedNodeMap attributes = element.getAttributes(); |
| int length = attributes.getLength(); |
| if (length > 0) { |
| StringBuffer buffer = new StringBuffer(); |
| for (int i = 0; i < length; i++) { |
| printAttributeWithPrefix((Attr) attributes.item(i), buffer); |
| } |
| for (int i = 0; i < length; i++) { |
| hasAttribute = printAttributeWithoutPrefix((Attr) attributes.item(i), hasAttribute); |
| } |
| if (buffer.length() > 0) { |
| if (hasAttribute) { |
| print(", "); |
| } |
| print(buffer.toString()); |
| hasAttribute = true; |
| } |
| } |
| return hasAttribute; |
| } |
| |
| private void printAttributeWithPrefix(Attr attribute, StringBuffer buffer) { |
| String prefix = attribute.getPrefix(); |
| if (prefix != null && prefix.length() > 0 && !prefix.equals("xmlns")) { |
| if (buffer.length() > 0) { |
| buffer.append(", "); |
| } |
| buffer.append(qt); |
| buffer.append(prefix); |
| buffer.append("."); |
| buffer.append(getLocalName(attribute)); |
| buffer.append(qt + ":" + qt); |
| buffer.append(escapeQuote(getAttributeValue(attribute))); |
| buffer.append(qt); |
| } |
| } |
| |
| private String getAttributeValue(Attr attribute) { |
| return attribute.getValue(); |
| } |
| |
| private boolean printAttributeWithoutPrefix(Attr attribute, boolean hasAttribute) { |
| String prefix = attribute.getPrefix(); |
| if (prefix == null || prefix.length() == 0) { |
| if (!hasAttribute) { |
| hasAttribute = true; |
| } else { |
| print(", "); |
| } |
| String localName = getLocalName(attribute); |
| boolean needsEscaping = checkEscaping(localName); |
| if (needsEscaping) print(qt); |
| print(localName); |
| if (needsEscaping) print(qt); |
| print(":"); |
| printQuoted(getAttributeValue(attribute)); |
| } |
| return hasAttribute; |
| } |
| |
| private boolean checkEscaping(String localName) { |
| return keywords.contains(localName) || localName.indexOf("-") != -1; |
| } |
| |
| protected String getTextNodeData(Text node) { |
| return node.getData().trim(); |
| } |
| |
| protected boolean mixedContent(NodeList list) { |
| boolean hasText = false; |
| boolean hasElement = false; |
| for (int i = 0, size = list.getLength(); i < size; i++) { |
| Node node = list.item(i); |
| if (node instanceof Element) { |
| hasElement = true; |
| } else if (node instanceof Text) { |
| String text = getTextNodeData((Text) node); |
| if (text.length() > 0) { |
| hasText = true; |
| } |
| } |
| } |
| return hasText && hasElement; |
| } |
| |
| protected void printChildren(Node parent, Map namespaces) { |
| for (Node node = parent.getFirstChild(); node != null; node = node.getNextSibling()) { |
| print(node, namespaces, false); |
| } |
| } |
| |
| protected String getLocalName(Node node) { |
| String answer = node.getLocalName(); |
| if (answer == null) { |
| answer = node.getNodeName(); |
| } |
| return answer.trim(); |
| } |
| |
| protected void printEnd(String text, boolean endWithComma) { |
| if (endWithComma) { |
| print(text); |
| println(","); |
| } else { |
| println(text); |
| } |
| } |
| |
| protected void println(String text) { |
| out.println(text); |
| } |
| |
| protected void print(String text) { |
| out.print(text); |
| } |
| |
| protected void printIndent() { |
| out.printIndent(); |
| } |
| } |