001: package groovy.text;
002:
003: import groovy.lang.Binding;
004: import groovy.lang.GroovyShell;
005: import groovy.lang.Script;
006: import groovy.lang.Writable;
007: import groovy.util.IndentPrinter;
008: import groovy.util.Node;
009: import groovy.util.XmlNodePrinter;
010: import groovy.util.XmlParser;
011: import groovy.xml.QName;
012:
013: import java.io.IOException;
014: import java.io.PrintWriter;
015: import java.io.Reader;
016: import java.io.StringWriter;
017: import java.io.Writer;
018: import java.lang.ref.WeakReference;
019: import java.util.HashMap;
020: import java.util.Map;
021:
022: import javax.xml.parsers.ParserConfigurationException;
023:
024: import org.codehaus.groovy.control.CompilationFailedException;
025: import org.codehaus.groovy.runtime.InvokerHelper;
026: import org.xml.sax.SAXException;
027:
028: /**
029: * Template engine for xml data input.
030: *
031: * @author Christian Stein
032: */
033: public class XmlTemplateEngine extends TemplateEngine {
034:
035: private static class GspPrinter extends XmlNodePrinter {
036:
037: public GspPrinter(PrintWriter out, String indent) {
038: this (new IndentPrinter(out, indent));
039: }
040:
041: public GspPrinter(IndentPrinter out) {
042: super (out, "\\\"");
043: }
044:
045: protected void printGroovyTag(String tag, String text) {
046: if (tag.equals("scriptlet")) {
047: out.print(text);
048: out.print("\n");
049: return;
050: }
051: if (tag.equals("expression")) {
052: printLineBegin();
053: out.print("${");
054: out.print(text);
055: out.print("}");
056: printLineEnd();
057: return;
058: }
059: throw new RuntimeException("Unsupported tag named \"" + tag
060: + "\".");
061: }
062:
063: protected void printLineBegin() {
064: out.print("out.print(\"");
065: out.printIndent();
066: }
067:
068: protected void printLineEnd(String comment) {
069: out.print("\\n\");");
070: if (comment != null) {
071: out.print(" // ");
072: out.print(comment);
073: }
074: out.print("\n");
075: }
076:
077: protected boolean printSpecialNode(Node node) {
078: Object name = node.name();
079: if (name != null && name instanceof QName) {
080: /*
081: * FIXME Somethings wrong with the SAX- or XMLParser. Prefix should only contain 'gsp'?!
082: */
083: String s = ((QName) name).getPrefix();
084: if (s.startsWith("gsp:")) {
085: s = s.substring(4); // 4 = "gsp:".length()
086: if (s.length() == 0) {
087: throw new RuntimeException(
088: "No local part after 'gsp:' given in node "
089: + node);
090: }
091: printGroovyTag(s, node.text());
092: return true;
093: }
094: }
095: return false;
096: }
097:
098: }
099:
100: private static class XmlTemplate implements Template {
101:
102: private final Script script;
103:
104: public XmlTemplate(Script script) {
105: this .script = script;
106: }
107:
108: public Writable make() {
109: return make(new HashMap());
110: }
111:
112: public Writable make(Map map) {
113: if (map == null) {
114: throw new IllegalArgumentException(
115: "map must not be null");
116: }
117: return new XmlWritable(script, new Binding(map));
118: }
119:
120: }
121:
122: private static class XmlWritable implements Writable {
123:
124: private final Binding binding;
125: private final Script script;
126: private WeakReference result;
127:
128: public XmlWritable(Script script, Binding binding) {
129: this .script = script;
130: this .binding = binding;
131: this .result = new WeakReference(null);
132: }
133:
134: public Writer writeTo(Writer out) {
135: Script scriptObject = InvokerHelper.createScript(script
136: .getClass(), binding);
137: PrintWriter pw = new PrintWriter(out);
138: scriptObject.setProperty("out", pw);
139: scriptObject.run();
140: pw.flush();
141: return out;
142: }
143:
144: public String toString() {
145: if (result.get() != null) {
146: return result.get().toString();
147: }
148: String string = writeTo(new StringWriter(1024)).toString();
149: result = new WeakReference(string);
150: return string;
151: }
152:
153: }
154:
155: public static final String DEFAULT_INDENTION = " ";
156:
157: private final GroovyShell groovyShell;
158: private final XmlParser xmlParser;
159: private String indention;
160:
161: public XmlTemplateEngine() throws SAXException,
162: ParserConfigurationException {
163: this (DEFAULT_INDENTION, false);
164: }
165:
166: public XmlTemplateEngine(String indention, boolean validating)
167: throws SAXException, ParserConfigurationException {
168: this (new XmlParser(validating, true), new GroovyShell(),
169: indention);
170: }
171:
172: public XmlTemplateEngine(XmlParser xmlParser,
173: GroovyShell groovyShell, String indention) {
174: this .groovyShell = groovyShell;
175: this .xmlParser = xmlParser;
176: this .indention = indention;
177: }
178:
179: public Template createTemplate(Reader reader)
180: throws CompilationFailedException, ClassNotFoundException,
181: IOException {
182: Node root = null;
183: try {
184: root = xmlParser.parse(reader);
185: } catch (SAXException e) {
186: throw new RuntimeException("Parsing XML source failed.", e);
187: }
188:
189: if (root == null) {
190: throw new IOException(
191: "Parsing XML source failed: root node is null.");
192: }
193:
194: // new NodePrinter().print(root);
195: // new XmlNodePrinter().print(root);
196:
197: StringWriter writer = new StringWriter(1024);
198: writer.write("/* Generated by XmlTemplateEngine */\n");
199: new GspPrinter(new PrintWriter(writer), DEFAULT_INDENTION)
200: .print(root);
201: String scriptText = writer.toString();
202:
203: // System.err.println("\n-\n" + scriptText + "\n-\n");
204:
205: Script script = groovyShell.parse(scriptText);
206: Template template = new XmlTemplate(script);
207: return template;
208: }
209:
210: public String getIndention() {
211: return indention;
212: }
213:
214: public void setIndention(String indention) {
215: if (indention == null) {
216: indention = DEFAULT_INDENTION;
217: }
218: this .indention = indention;
219: }
220:
221: public String toString() {
222: return "XmlTemplateEngine";
223: }
224:
225: }
|