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, (NodeChild) root);
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((Iterable) attrs, ", "));
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, (NodeChild) child);
193 } else if (child instanceof Node) {
194 walkXml(printer, (Node) child);
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((Iterable) attrs, ", "));
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, (NodeChild) child);
228 } else if (child instanceof Node) {
229 walkXml(printer, (Node) child);
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 }
|