| 
001 /*002  * Copyright 2008-2015 the original author or authors.
 003  *
 004  * Licensed under the Apache License, Version 2.0 (the "License");
 005  * you may not use this file except in compliance with the License.
 006  * You may obtain a copy of the License at
 007  *
 008  *     http://www.apache.org/licenses/LICENSE-2.0
 009  *
 010  * Unless required by applicable law or agreed to in writing, software
 011  * distributed under the License is distributed on an "AS IS" BASIS,
 012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 013  * See the License for the specific language governing permissions and
 014  * limitations under the License.
 015  */
 016 package griffon.util;
 017
 018 import groovy.util.IndentPrinter;
 019 import groovy.util.XmlSlurper;
 020 import groovy.util.slurpersupport.GPathResult;
 021 import groovy.util.slurpersupport.Node;
 022 import groovy.util.slurpersupport.NodeChild;
 023 import org.codehaus.groovy.runtime.DefaultGroovyMethods;
 024 import org.xml.sax.InputSource;
 025 import org.xml.sax.SAXException;
 026
 027 import javax.xml.parsers.ParserConfigurationException;
 028 import java.io.ByteArrayOutputStream;
 029 import java.io.File;
 030 import java.io.IOException;
 031 import java.io.InputStream;
 032 import java.io.OutputStream;
 033 import java.io.OutputStreamWriter;
 034 import java.io.PrintWriter;
 035 import java.io.Reader;
 036 import java.util.ArrayList;
 037 import java.util.Iterator;
 038 import java.util.List;
 039 import java.util.Map;
 040
 041 /**
 042  * Translates an XML file into a Groovy script that is suitable for a Groovy builder.
 043  * String literals must be escaped either using single or double quotes. <p>
 044  * This helper class is useful for translating an XML View definition into a Groovy
 045  * script that can be handled by an UberBuilder, for example this View
 046  *
 047  * <xmp>
 048     <application title="app.config.application.title"
 049                  pack="true">
 050         <actions>
 051             <action id="'clickAction'"
 052                     name="'Click'"
 053                     closure="{controller.click(it)}"/>
 054         </actions>
 055
 056         <gridLayout cols="1" rows="3"/>
 057         <textField id="'input'" columns="20"
 058             text="bind('value', target: model)"/>
 059         <textField id="'output'" columns="20"
 060             text="bind{model.value}" editable="false"/>
 061         <button action="clickAction"/>
 062     </application>
 063  * </xmp>
 064  *
 065  * results in the following script
 066  *
 067  * <pre>
 068 application(title: app.config.application.title, pack: true) {
 069   actions {
 070     action(id: 'clickAction', name: 'Click', closure: {controller.click(it)})
 071   }
 072   gridLayout(cols: 1, rows: 3)
 073   textField(id: 'input', text: bind('value', target: model), columns: 20)
 074   textField(id: 'output', text: bind{model.value}, columns: 20, editable: false)
 075   button(action: clickAction)
 076 }
 077  * </pre>
 078  *
 079  * @author Andres Almiray
 080  */
 081 public final class Xml2Groovy {
 082     private static final Xml2Groovy INSTANCE;
 083
 084     static {
 085         INSTANCE = new Xml2Groovy();
 086     }
 087
 088     public static Xml2Groovy getInstance() {
 089         return INSTANCE;
 090     }
 091
 092     private Xml2Groovy() {
 093
 094     }
 095
 096     public String parse(File file) {
 097         try {
 098             return translate(newXmlSlurper().parse(file));
 099         } catch (IOException | SAXException e) {
 100             throw new IllegalArgumentException(e);
 101         }
 102     }
 103
 104     public String parse(InputSource source) {
 105         try {
 106             return translate(newXmlSlurper().parse(source));
 107         } catch (IOException | SAXException e) {
 108             throw new IllegalArgumentException(e);
 109         }
 110     }
 111
 112     public String parse(InputStream stream) {
 113         try {
 114             return translate(newXmlSlurper().parse(stream));
 115         } catch (IOException | SAXException e) {
 116             throw new IllegalArgumentException(e);
 117         }
 118     }
 119
 120     public String parse(Reader reader) {
 121         try {
 122             return translate(newXmlSlurper().parse(reader));
 123         } catch (IOException | SAXException e) {
 124             throw new IllegalArgumentException(e);
 125         }
 126     }
 127
 128     public String parse(String uri) {
 129         try {
 130             return translate(newXmlSlurper().parse(uri));
 131         } catch (IOException | SAXException e) {
 132             throw new IllegalArgumentException(e);
 133         }
 134     }
 135
 136     public String parse(GPathResult root) {
 137         return translate(root);
 138     }
 139
 140     public String parseText(String text) {
 141         try {
 142             return translate(newXmlSlurper().parseText(text));
 143         } catch (IOException | SAXException e) {
 144             throw new IllegalArgumentException(e);
 145         }
 146     }
 147
 148     private XmlSlurper newXmlSlurper() {
 149         try {
 150             return new XmlSlurper();
 151         } catch (ParserConfigurationException | SAXException e) {
 152             throw new IllegalArgumentException(e);
 153         }
 154     }
 155
 156     private String translate(GPathResult root) {
 157         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 158         IndentPrinter printer = createIndentPrinter(baos);
 159         walkXml(printer, (NodeChild) root);
 160         printer.flush();
 161
 162         return baos.toString();
 163     }
 164
 165     private IndentPrinter createIndentPrinter(OutputStream os) {
 166         PrintWriter pw = new PrintWriter(new OutputStreamWriter(os));
 167         return new IndentPrinter(pw, "    ");
 168     }
 169
 170     private void walkXml(IndentPrinter printer, NodeChild node) {
 171         printer.printIndent();
 172         printer.print(node.name());
 173         if (!node.attributes().isEmpty()) {
 174             printer.print("(");
 175             List<String> attrs = new ArrayList<>();
 176             for (Object o : node.attributes().entrySet()) {
 177                 Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
 178                 attrs.add(entry.getKey() + ": " + entry.getValue());
 179             }
 180             printer.print(DefaultGroovyMethods.join((Iterable) attrs, ", "));
 181             printer.print(")");
 182         }
 183
 184         if (node.children().size() > 0) {
 185             printer.println(" {");
 186             printer.incrementIndent();
 187             for (Iterator<?> iter = node.childNodes(); iter.hasNext(); ) {
 188                 Object child = iter.next();
 189                 if (child instanceof NodeChild) {
 190                     walkXml(printer, (NodeChild) child);
 191                 } else if (child instanceof Node) {
 192                     walkXml(printer, (Node) child);
 193                 }
 194             }
 195             printer.decrementIndent();
 196             printer.printIndent();
 197             printer.println("}");
 198         } else if (!node.attributes().isEmpty()) {
 199             printer.println("");
 200         } else {
 201             printer.println("()");
 202         }
 203     }
 204
 205     private void walkXml(IndentPrinter printer, Node node) {
 206         printer.printIndent();
 207         printer.print(node.name());
 208         if (!node.attributes().isEmpty()) {
 209             printer.print("(");
 210             List<String> attrs = new ArrayList<>();
 211             for (Object o : node.attributes().entrySet()) {
 212                 Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
 213                 attrs.add(entry.getKey() + ": " + entry.getValue());
 214             }
 215             printer.print(DefaultGroovyMethods.join((Iterable) attrs, ", "));
 216             printer.print(")");
 217         }
 218
 219         if (node.children().size() > 0) {
 220             printer.println(" {");
 221             printer.incrementIndent();
 222             for (Iterator<?> iter = node.childNodes(); iter.hasNext(); ) {
 223                 Object child = iter.next();
 224                 if (child instanceof NodeChild) {
 225                     walkXml(printer, (NodeChild) child);
 226                 } else if (child instanceof Node) {
 227                     walkXml(printer, (Node) child);
 228                 }
 229             }
 230             printer.decrementIndent();
 231             printer.printIndent();
 232             printer.println("}");
 233         } else if (!node.attributes().isEmpty()) {
 234             printer.println("");
 235         } else {
 236             printer.println("()");
 237         }
 238     }
 239 }
 |