View Javadoc

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                   * FIXME Somethings wrong with the SAX- or XMLParser. Prefix should only contain 'gsp'?!
81                   */
82                  String s = ((QName) name).getPrefix();
83                  if (s.startsWith("gsp:")) {
84                      s = s.substring(4); // 4 = "gsp:".length()
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         // new NodePrinter().print(root);
183         // new XmlNodePrinter().print(root);
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         // System.err.println("\n-\n" + scriptText + "\n-\n");
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 }