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 }