Xml2Groovy.java
001 /*
002  * Copyright 2008-2014 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.*;
029 import java.util.ArrayList;
030 import java.util.Iterator;
031 import java.util.List;
032 import java.util.Map;
033 
034 /**
035  * Translates an XML file into a Groovy script that is suitable for a Groovy builder.
036  * String literals must be escaped either using single or double quotes. <p>
037  * This helper class is useful for translating an XML View definition into a Groovy
038  * script that can be handled by an UberBuilder, for example this View
039  *
040  <xmp>
041     <application title="app.config.application.title"
042                  pack="true">
043         <actions>
044             <action id="'clickAction'"
045                     name="'Click'"
046                     closure="{controller.click(it)}"/>
047         </actions>
048     
049         <gridLayout cols="1" rows="3"/>
050         <textField id="'input'" columns="20"
051             text="bind('value', target: model)"/>
052         <textField id="'output'" columns="20"
053             text="bind{model.value}" editable="false"/>
054         <button action="clickAction"/>
055     </application>
056  </xmp>
057  *
058  * results in the following script
059  *
060  <pre>
061 application(title: app.config.application.title, pack: true) {
062   actions {
063     action(id: 'clickAction', name: 'Click', closure: {controller.click(it)})
064   }
065   gridLayout(cols: 1, rows: 3)
066   textField(id: 'input', text: bind('value', target: model), columns: 20)
067   textField(id: 'output', text: bind{model.value}, columns: 20, editable: false)
068   button(action: clickAction)
069 }
070  </pre>
071  *
072  @author Andres Almiray
073  */
074 public final class Xml2Groovy {
075     private static final Xml2Groovy INSTANCE;
076 
077     static {
078         INSTANCE = new Xml2Groovy();
079     }
080 
081     public static Xml2Groovy getInstance() {
082         return INSTANCE;
083     }
084 
085     private Xml2Groovy() {
086 
087     }
088 
089     public String parse(File file) {
090         try {
091             return translate(newXmlSlurper().parse(file));
092         catch (IOException | SAXException e) {
093             throw new IllegalArgumentException(e);
094         }
095     }
096 
097     public String parse(InputSource source) {
098         try {
099             return translate(newXmlSlurper().parse(source));
100         catch (IOException | SAXException e) {
101             throw new IllegalArgumentException(e);
102         }
103     }
104 
105     public String parse(InputStream stream) {
106         try {
107             return translate(newXmlSlurper().parse(stream));
108         catch (IOException | SAXException e) {
109             throw new IllegalArgumentException(e);
110         }
111     }
112 
113     public String parse(Reader reader) {
114         try {
115             return translate(newXmlSlurper().parse(reader));
116         catch (IOException | SAXException e) {
117             throw new IllegalArgumentException(e);
118         }
119     }
120 
121     public String parse(String uri) {
122         try {
123             return translate(newXmlSlurper().parse(uri));
124         catch (IOException | SAXException e) {
125             throw new IllegalArgumentException(e);
126         }
127     }
128 
129     public String parse(GPathResult root) {
130         return translate(root);
131     }
132 
133     public String parseText(String text) {
134         try {
135             return translate(newXmlSlurper().parseText(text));
136         catch (IOException | SAXException e) {
137             throw new IllegalArgumentException(e);
138         }
139     }
140 
141     private XmlSlurper newXmlSlurper() {
142         try {
143             return new XmlSlurper();
144         catch (ParserConfigurationException | SAXException e) {
145             throw new IllegalArgumentException(e);
146         }
147     }
148 
149     private String translate(GPathResult root) {
150         ByteArrayOutputStream baos = new ByteArrayOutputStream();
151         IndentPrinter printer = createIndentPrinter(baos);
152         walkXml(printer, (NodeChildroot);
153         printer.flush();
154 
155         return baos.toString();
156     }
157 
158     private IndentPrinter createIndentPrinter(OutputStream os) {
159         PrintWriter pw = new PrintWriter(new OutputStreamWriter(os));
160         return new IndentPrinter(pw, "    ");
161     }
162 
163     private void walkXml(IndentPrinter printer, NodeChild node) {
164         printer.printIndent();
165         printer.print(node.name());
166         if (!node.attributes().isEmpty()) {
167             printer.print("(");
168             List<String> attrs = new ArrayList<>();
169             for (Object o : node.attributes().entrySet()) {
170                 Map.Entry<?, ?> entry = (Map.Entry<?, ?>o;
171                 attrs.add(entry.getKey() ": " + entry.getValue());
172             }
173             printer.print(DefaultGroovyMethods.join((Iterableattrs, ", "));
174             printer.print(")");
175         }
176 
177         if (node.children().size() 0) {
178             printer.println(" {");
179             printer.incrementIndent();
180             for (Iterator<?> iter = node.childNodes(); iter.hasNext()) {
181                 Object child = iter.next();
182                 if (child instanceof NodeChild) {
183                     walkXml(printer, (NodeChildchild);
184                 else if (child instanceof Node) {
185                     walkXml(printer, (Nodechild);
186                 }
187             }
188             printer.decrementIndent();
189             printer.printIndent();
190             printer.println("}");
191         else if (!node.attributes().isEmpty()) {
192             printer.println("");
193         else {
194             printer.println("()");
195         }
196     }
197 
198     private void walkXml(IndentPrinter printer, Node node) {
199         printer.printIndent();
200         printer.print(node.name());
201         if (!node.attributes().isEmpty()) {
202             printer.print("(");
203             List<String> attrs = new ArrayList<>();
204             for (Object o : node.attributes().entrySet()) {
205                 Map.Entry<?, ?> entry = (Map.Entry<?, ?>o;
206                 attrs.add(entry.getKey() ": " + entry.getValue());
207             }
208             printer.print(DefaultGroovyMethods.join((Iterableattrs, ", "));
209             printer.print(")");
210         }
211 
212         if (node.children().size() 0) {
213             printer.println(" {");
214             printer.incrementIndent();
215             for (Iterator<?> iter = node.childNodes(); iter.hasNext()) {
216                 Object child = iter.next();
217                 if (child instanceof NodeChild) {
218                     walkXml(printer, (NodeChildchild);
219                 else if (child instanceof Node) {
220                     walkXml(printer, (Nodechild);
221                 }
222             }
223             printer.decrementIndent();
224             printer.printIndent();
225             printer.println("}");
226         else if (!node.attributes().isEmpty()) {
227             printer.println("");
228         else {
229             printer.println("()");
230         }
231     }
232 }