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 }
|