Xml2Groovy.java
001 /*
002  * Copyright 2011-2012 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 e) {
093             throw new IllegalArgumentException(e);
094         catch (SAXException e) {
095             throw new IllegalArgumentException(e);
096         }
097     }
098 
099     public String parse(InputSource source)  {
100         try {
101             return translate(newXmlSlurper().parse(source));
102         catch (IOException e) {
103             throw new IllegalArgumentException(e);
104         catch (SAXException e) {
105             throw new IllegalArgumentException(e);
106         }
107     }
108 
109     public String parse(InputStream stream)  {
110         try {
111             return translate(newXmlSlurper().parse(stream));
112         catch (IOException e) {
113             throw new IllegalArgumentException(e);
114         catch (SAXException e) {
115             throw new IllegalArgumentException(e);
116         }
117     }
118 
119     public String parse(Reader reader)  {
120         try {
121             return translate(newXmlSlurper().parse(reader));
122         catch (IOException e) {
123             throw new IllegalArgumentException(e);
124         catch (SAXException e) {
125             throw new IllegalArgumentException(e);
126         }
127     }
128 
129     public String parse(String uri)  {
130         try {
131             return translate(newXmlSlurper().parse(uri));
132         catch (IOException e) {
133             throw new IllegalArgumentException(e);
134         catch (SAXException e) {
135             throw new IllegalArgumentException(e);
136         }
137     }
138 
139     public String parse(GPathResult root) {
140         return translate(root);
141     }
142 
143     public String parseText(String text)  {
144         try {
145             return translate(newXmlSlurper().parseText(text));
146         catch (IOException e) {
147             throw new IllegalArgumentException(e);
148         catch (SAXException e) {
149             throw new IllegalArgumentException(e);
150         }
151     }
152 
153     private XmlSlurper newXmlSlurper() {
154         try {
155             return new XmlSlurper();
156         catch (ParserConfigurationException e) {
157             throw new IllegalArgumentException(e);
158         catch (SAXException e) {
159             throw new IllegalArgumentException(e);
160         }
161     }
162 
163     private String translate(GPathResult root) {
164         ByteArrayOutputStream baos = new ByteArrayOutputStream();
165         IndentPrinter printer = createIndentPrinter(baos);
166         walkXml(printer, (NodeChildroot);
167         printer.flush();
168 
169         return baos.toString();
170     }
171 
172     private IndentPrinter createIndentPrinter(OutputStream os) {
173         PrintWriter pw = new PrintWriter(new OutputStreamWriter(os));
174         return new IndentPrinter(pw);
175     }
176 
177     private void walkXml(IndentPrinter printer, NodeChild node) {
178         printer.printIndent();
179         printer.print(node.name());
180         if (!node.attributes().isEmpty()) {
181             printer.print("(");
182             List<String> attrs = new ArrayList<String>();
183             for (Object o : node.attributes().entrySet()) {
184                 Map.Entry entry = (Map.Entryo;
185                 attrs.add(entry.getKey() ": " + entry.getValue());
186             }
187             printer.print(DefaultGroovyMethods.join(attrs, ","));
188             printer.print(")");
189         }
190 
191         if (node.children().size() 0) {
192             printer.println(" {");
193             printer.incrementIndent();
194             for (Iterator iter = node.childNodes(); iter.hasNext()) {
195                 Object child = iter.next();
196                 if (child instanceof NodeChild) {
197                     walkXml(printer, (NodeChildchild);
198                 else if (child instanceof Node) {
199                     walkXml(printer, (Nodechild);
200                 }
201             }
202             printer.decrementIndent();
203             printer.printIndent();
204             printer.println("}");
205         else if (!node.attributes().isEmpty()) {
206             printer.println("");
207         else {
208             printer.println("()");
209         }
210     }
211 
212     private void walkXml(IndentPrinter printer, Node node) {
213         printer.printIndent();
214         printer.print(node.name());
215         if (!node.attributes().isEmpty()) {
216             printer.print("(");
217             List<String> attrs = new ArrayList<String>();
218             for (Object o : node.attributes().entrySet()) {
219                 Map.Entry entry = (Map.Entryo;
220                 attrs.add(entry.getKey() ": " + entry.getValue());
221             }
222             printer.print(DefaultGroovyMethods.join(attrs, ","));
223             printer.print(")");
224         }
225 
226         if (node.children().size() 0) {
227             printer.println(" {");
228             printer.incrementIndent();
229             for (Iterator iter = node.childNodes(); iter.hasNext()) {
230                 Object child = iter.next();
231                 if (child instanceof NodeChild) {
232                     walkXml(printer, (NodeChildchild);
233                 else if (child instanceof Node) {
234                     walkXml(printer, (Nodechild);
235                 }
236             }
237             printer.decrementIndent();
238             printer.printIndent();
239             printer.println("}");
240         else if (!node.attributes().isEmpty()) {
241             printer.println("");
242         else {
243             printer.println("()");
244         }
245     }
246 }