/* * Copyright 2008-2017 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 griffon.util; import groovy.util.IndentPrinter; import groovy.util.XmlSlurper; import groovy.util.slurpersupport.GPathResult; import groovy.util.slurpersupport.Node; import groovy.util.slurpersupport.NodeChild; import org.codehaus.groovy.runtime.DefaultGroovyMethods; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.Reader; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Translates an XML file into a Groovy script that is suitable for a Groovy builder. * String literals must be escaped either using single or double quotes. <p> * This helper class is useful for translating an XML View definition into a Groovy * script that can be handled by an UberBuilder, for example this View * * <xmp> <application title="app.config.application.title" pack="true"> <actions> <action id="'clickAction'" name="'Click'" closure="{controller.click(it)}"/> </actions> <gridLayout cols="1" rows="3"/> <textField id="'input'" columns="20" text="bind('value', target: model)"/> <textField id="'output'" columns="20" text="bind{model.value}" editable="false"/> <button action="clickAction"/> </application> * </xmp> * * results in the following script * * <pre> application(title: app.config.application.title, pack: true) { actions { action(id: 'clickAction', name: 'Click', closure: {controller.click(it)}) } gridLayout(cols: 1, rows: 3) textField(id: 'input', text: bind('value', target: model), columns: 20) textField(id: 'output', text: bind{model.value}, columns: 20, editable: false) button(action: clickAction) } * </pre> * * @author Andres Almiray */ public final class Xml2Groovy { private static final Xml2Groovy INSTANCE; static { INSTANCE = new Xml2Groovy(); } public static Xml2Groovy getInstance() { return INSTANCE; } private Xml2Groovy() { } public String parse(File file) { try { return translate(newXmlSlurper().parse(file)); } catch (IOException | SAXException e) { throw new IllegalArgumentException(e); } } public String parse(InputSource source) { try { return translate(newXmlSlurper().parse(source)); } catch (IOException | SAXException e) { throw new IllegalArgumentException(e); } } public String parse(InputStream stream) { try { return translate(newXmlSlurper().parse(stream)); } catch (IOException | SAXException e) { throw new IllegalArgumentException(e); } } public String parse(Reader reader) { try { return translate(newXmlSlurper().parse(reader)); } catch (IOException | SAXException e) { throw new IllegalArgumentException(e); } } public String parse(String uri) { try { return translate(newXmlSlurper().parse(uri)); } catch (IOException | SAXException e) { throw new IllegalArgumentException(e); } } public String parse(GPathResult root) { return translate(root); } public String parseText(String text) { try { return translate(newXmlSlurper().parseText(text)); } catch (IOException | SAXException e) { throw new IllegalArgumentException(e); } } private XmlSlurper newXmlSlurper() { try { return new XmlSlurper(); } catch (ParserConfigurationException | SAXException e) { throw new IllegalArgumentException(e); } } private String translate(GPathResult root) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); IndentPrinter printer = createIndentPrinter(baos); walkXml(printer, (NodeChild) root); printer.flush(); return baos.toString(); } private IndentPrinter createIndentPrinter(OutputStream os) { PrintWriter pw = new PrintWriter(new OutputStreamWriter(os)); return new IndentPrinter(pw, " "); } private void walkXml(IndentPrinter printer, NodeChild node) { printer.printIndent(); printer.print(node.name()); if (!node.attributes().isEmpty()) { printer.print("("); List<String> attrs = new ArrayList<>(); for (Object o : node.attributes().entrySet()) { Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o; attrs.add(entry.getKey() + ": " + entry.getValue()); } printer.print(DefaultGroovyMethods.join((Iterable) attrs, ", ")); printer.print(")"); } if (node.children().size() > 0) { printer.println(" {"); printer.incrementIndent(); for (Iterator<?> iter = node.childNodes(); iter.hasNext(); ) { Object child = iter.next(); if (child instanceof NodeChild) { walkXml(printer, (NodeChild) child); } else if (child instanceof Node) { walkXml(printer, (Node) child); } } printer.decrementIndent(); printer.printIndent(); printer.println("}"); } else if (!node.attributes().isEmpty()) { printer.println(""); } else { printer.println("()"); } } private void walkXml(IndentPrinter printer, Node node) { printer.printIndent(); printer.print(node.name()); if (!node.attributes().isEmpty()) { printer.print("("); List<String> attrs = new ArrayList<>(); for (Object o : node.attributes().entrySet()) { Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o; attrs.add(entry.getKey() + ": " + entry.getValue()); } printer.print(DefaultGroovyMethods.join((Iterable) attrs, ", ")); printer.print(")"); } if (node.children().size() > 0) { printer.println(" {"); printer.incrementIndent(); for (Iterator<?> iter = node.childNodes(); iter.hasNext(); ) { Object child = iter.next(); if (child instanceof NodeChild) { walkXml(printer, (NodeChild) child); } else if (child instanceof Node) { walkXml(printer, (Node) child); } } printer.decrementIndent(); printer.printIndent(); printer.println("}"); } else if (!node.attributes().isEmpty()) { printer.println(""); } else { printer.println("()"); } } }