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, (NodeChild) root);
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((Iterable) attrs, ", "));
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, (NodeChild) child);
184 } else if (child instanceof Node) {
185 walkXml(printer, (Node) child);
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((Iterable) attrs, ", "));
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, (NodeChild) child);
219 } else if (child instanceof Node) {
220 walkXml(printer, (Node) child);
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 }
|