1 package groovy.text;
2
3 import groovy.lang.Binding;
4 import groovy.lang.GroovyShell;
5 import groovy.lang.Script;
6 import groovy.lang.Writable;
7 import groovy.util.IndentPrinter;
8 import groovy.util.Node;
9 import groovy.util.XmlNodePrinter;
10 import groovy.util.XmlParser;
11 import groovy.xml.QName;
12
13 import java.io.IOException;
14 import java.io.PrintWriter;
15 import java.io.Reader;
16 import java.io.StringWriter;
17 import java.io.Writer;
18 import java.lang.ref.WeakReference;
19 import java.util.HashMap;
20 import java.util.Map;
21
22 import javax.xml.parsers.ParserConfigurationException;
23
24 import org.codehaus.groovy.control.CompilationFailedException;
25 import org.codehaus.groovy.runtime.InvokerHelper;
26 import org.xml.sax.SAXException;
27
28 /***
29 * Template engine for xml data input.
30 *
31 * @author Christian Stein
32 */
33 public class XmlTemplateEngine extends TemplateEngine {
34
35 private static class GspPrinter extends XmlNodePrinter {
36
37 public GspPrinter(PrintWriter out, String indent) {
38 this(new IndentPrinter(out, indent));
39 }
40
41 public GspPrinter(IndentPrinter out) {
42 super(out, "//\"");
43 }
44
45 protected void printGroovyTag(String tag, String text) {
46 if (tag.equals("scriptlet")) {
47 out.print(text);
48 out.print("\n");
49 return;
50 }
51 if (tag.equals("expression")) {
52 printLineBegin();
53 out.print("${");
54 out.print(text);
55 out.print("}");
56 printLineEnd();
57 return;
58 }
59 throw new RuntimeException("Unsupported tag named \"" + tag + "\".");
60 }
61
62 protected void printLineBegin() {
63 out.print("out.print(\"");
64 out.printIndent();
65 }
66
67 protected void printLineEnd(String comment) {
68 out.print("//n\");");
69 if (comment != null) {
70 out.print(" // ");
71 out.print(comment);
72 }
73 out.print("\n");
74 }
75
76 protected boolean printSpecialNode(Node node) {
77 Object name = node.name();
78 if (name != null && name instanceof QName) {
79
80
81
82 String s = ((QName) name).getPrefix();
83 if (s.startsWith("gsp:")) {
84 s = s.substring(4);
85 if (s.length() == 0) {
86 throw new RuntimeException("No local part after 'gsp:' given in node " + node);
87 }
88 printGroovyTag(s, node.text());
89 return true;
90 }
91 }
92 return false;
93 }
94
95 }
96
97 private static class XmlTemplate implements Template {
98
99 private final Script script;
100
101 public XmlTemplate(Script script) {
102 this.script = script;
103 }
104
105 public Writable make() {
106 return make(new HashMap());
107 }
108
109 public Writable make(Map map) {
110 if (map == null) {
111 throw new IllegalArgumentException("map must not be null");
112 }
113 return new XmlWritable(script, new Binding(map));
114 }
115
116 }
117
118 private static class XmlWritable implements Writable {
119
120 private final Binding binding;
121 private final Script script;
122 private WeakReference result;
123
124 public XmlWritable(Script script, Binding binding) {
125 this.script = script;
126 this.binding = binding;
127 this.result = new WeakReference(null);
128 }
129
130 public Writer writeTo(Writer out) {
131 Script scriptObject = InvokerHelper.createScript(script.getClass(), binding);
132 PrintWriter pw = new PrintWriter(out);
133 scriptObject.setProperty("out", pw);
134 scriptObject.run();
135 pw.flush();
136 return out;
137 }
138
139 public String toString() {
140 if (result.get() != null) {
141 return result.get().toString();
142 }
143 String string = writeTo(new StringWriter(1024)).toString();
144 result = new WeakReference(string);
145 return string;
146 }
147
148 }
149
150 public static final String DEFAULT_INDENTION = " ";
151
152 private final GroovyShell groovyShell;
153 private final XmlParser xmlParser;
154 private String indention;
155
156 public XmlTemplateEngine() throws SAXException, ParserConfigurationException {
157 this(DEFAULT_INDENTION, false);
158 }
159
160 public XmlTemplateEngine(String indention, boolean validating) throws SAXException, ParserConfigurationException {
161 this(new XmlParser(validating, true), new GroovyShell(), indention);
162 }
163
164 public XmlTemplateEngine(XmlParser xmlParser, GroovyShell groovyShell, String indention) {
165 this.groovyShell = groovyShell;
166 this.xmlParser = xmlParser;
167 this.indention = indention;
168 }
169
170 public Template createTemplate(Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException {
171 Node root = null;
172 try {
173 root = xmlParser.parse(reader);
174 } catch (SAXException e) {
175 throw new RuntimeException("Parsing XML source failed.", e);
176 }
177
178 if (root == null) {
179 throw new IOException("Parsing XML source failed: root node is null.");
180 }
181
182
183
184
185 StringWriter writer = new StringWriter(1024);
186 writer.write("/* Generated by XmlTemplateEngine */\n");
187 new GspPrinter(new PrintWriter(writer), DEFAULT_INDENTION).print(root);
188 String scriptText = writer.toString();
189
190
191
192 Script script = groovyShell.parse(scriptText);
193 Template template = new XmlTemplate(script);
194 return template;
195 }
196
197 public String getIndention() {
198 return indention;
199 }
200
201 public void setIndention(String indention) {
202 if (indention == null) {
203 indention = DEFAULT_INDENTION;
204 }
205 this.indention = indention;
206 }
207
208 public String toString() {
209 return "XmlTemplateEngine";
210 }
211
212 }