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, (NodeChild) root);
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.Entry) o;
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, (NodeChild) child);
198 } else if (child instanceof Node) {
199 walkXml(printer, (Node) child);
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.Entry) o;
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, (NodeChild) child);
233 } else if (child instanceof Node) {
234 walkXml(printer, (Node) child);
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 }
|