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