a tiny c-template work-alike for java
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

184 lines
4.9 KiB

  1. package com.binarythought.picotemplate;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import java.util.Stack;
  5. import java.util.regex.Pattern;
  6. import java.util.regex.Matcher;
  7. import java.io.FileReader;
  8. import java.io.File;
  9. /**
  10. * Template is the base class for picotemplate. It it used to generate
  11. * html or text from a supplied template and dictionary.
  12. * <p>Basic usage:
  13. * <p><code>Template template = new Template("I like {{FOOD}}.");<br>
  14. * TemplateDictionary dictionary = new TemplateDictionary();<br>
  15. * dictionary.put("food", "cookies");<br>
  16. * String result = template.parse(dictionary);</code>
  17. * <p>Value of result : <code>I like cookies.</code>
  18. */
  19. public class Template {
  20. private static final Pattern pattern = Pattern.compile(
  21. "\\{\\{(#|/){0,1}(\\w+)\\}\\}", Pattern.CANON_EQ
  22. );
  23. private TemplateNode parsedTemplate[];
  24. private String originalTemplate;
  25. /**
  26. * Instantiate and compile a new template with the template provided.
  27. * @param template String containing the template.
  28. */
  29. public Template(String template) throws Exception
  30. {
  31. this.originalTemplate = template;
  32. this.parsedTemplate = initTemplate(template);
  33. }
  34. /**
  35. * Instantiate and compile a new template with the template provided.
  36. * @param file File containing the template.
  37. */
  38. public Template(File file) throws Exception
  39. {
  40. if(file == null || !file.canRead()){
  41. throw new Exception("Cannot read "+file.getName());
  42. }
  43. FileReader reader = new FileReader(file);
  44. StringBuilder b = new StringBuilder();
  45. char buf[] = new char[1024];
  46. for(int i=0; (i = reader.read(buf)) != -1;){
  47. b.append(buf, 0, i);
  48. }
  49. reader.close();
  50. this.originalTemplate = b.toString();
  51. this.parsedTemplate = initTemplate(this.originalTemplate);
  52. }
  53. private static TemplateNode[] initTemplate(String template) throws Exception
  54. {
  55. ArrayList<TemplateNode> parsedTemplate = new ArrayList<TemplateNode>();
  56. Stack<String> sections = new Stack<String>();
  57. Matcher match = pattern.matcher(template);
  58. int lastpos = 0;
  59. while(match.find()){
  60. String segment = template.substring(lastpos,match.start());
  61. if(segment.length() > 0){
  62. parsedTemplate.add(new TemplateNode(TemplateNode.PLAIN, segment));
  63. }
  64. if("#".equals(match.group(1))){
  65. sections.push(match.group(2));
  66. parsedTemplate.add(
  67. new TemplateNode(TemplateNode.SECTION_START, match.group(2))
  68. );
  69. } else if("/".equals(match.group(1))){
  70. parsedTemplate.add(
  71. new TemplateNode(TemplateNode.SECTION_END, match.group(2))
  72. );
  73. if(sections.empty() || !sections.pop().equals(match.group(2))){
  74. throw new Exception("Out of turn section termation at "+match.start());
  75. }
  76. } else {
  77. parsedTemplate.add(
  78. new TemplateNode(TemplateNode.VARIABLE, match.group(2))
  79. );
  80. }
  81. lastpos = match.end();
  82. }
  83. String segment = template.substring(lastpos, template.length());
  84. if(segment.length() > 0){
  85. parsedTemplate.add(new TemplateNode(TemplateNode.PLAIN, segment));
  86. }
  87. if(!sections.empty()){
  88. throw new Exception("Unterminated section in template");
  89. }
  90. return parsedTemplate.toArray(new TemplateNode[0]);
  91. }
  92. /**
  93. * Parse this template with the provided dictionary and output it to a string.
  94. * <p><b>This method is threadsafe.</b>
  95. * @param dict Template dictionary to parse against.
  96. * @return String containing the parsed result of the template.
  97. */
  98. public String parse(TemplateDictionary dict)
  99. {
  100. StringBuilder b = new StringBuilder();
  101. if(dict != null){ parseTemplate(dict, b, parsedTemplate, 0); }
  102. else { parseTemplate(b, parsedTemplate); }
  103. return b.toString();
  104. }
  105. private static int parseTemplate(StringBuilder output, TemplateNode template[])
  106. {
  107. for(int i = 0; i < template.length; i++){
  108. if(template[i].getNodeType() == TemplateNode.PLAIN){
  109. output.append(template[i].getContent());
  110. }
  111. }
  112. return 0;
  113. }
  114. private static int parseTemplate(TemplateDictionary dict, StringBuilder output, TemplateNode template[], int i)
  115. {
  116. for(;i < template.length; i++){
  117. switch(template[i].getNodeType()){
  118. case TemplateNode.PLAIN:
  119. output.append(template[i].getContent());
  120. break;
  121. case TemplateNode.VARIABLE:
  122. output.append(dict.get(template[i].getContent()));
  123. break;
  124. case TemplateNode.SECTION_START:
  125. if(dict.isShown(template[i].getContent())){
  126. List<TemplateDictionary> l = (
  127. dict.getParent() == null ? dict.getChild(template[i].getContent()) :
  128. dict.getParent().getChild(template[i].getContent())
  129. );
  130. if(l == null){
  131. i = parseTemplate(dict, output, template, i+1);
  132. } else {
  133. int last = 0;
  134. for(TemplateDictionary d : l){
  135. last = parseTemplate(d, output, template, i+1);
  136. }
  137. i = last;
  138. }
  139. } else {
  140. for(int c=1; c > 0;){
  141. i++;
  142. switch(template[i].getNodeType()){
  143. case TemplateNode.SECTION_START: c++; break;
  144. case TemplateNode.SECTION_END: c--; break;
  145. }
  146. }
  147. }
  148. break;
  149. case TemplateNode.SECTION_END: return i;
  150. }
  151. }
  152. return 0;
  153. }
  154. }