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.
184 lines
4.9 KiB
184 lines
4.9 KiB
package com.binarythought.picotemplate;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Stack;
|
|
import java.util.regex.Pattern;
|
|
import java.util.regex.Matcher;
|
|
import java.io.FileReader;
|
|
import java.io.File;
|
|
|
|
/**
|
|
* Template is the base class for picotemplate. It it used to generate
|
|
* html or text from a supplied template and dictionary.
|
|
* <p>Basic usage:
|
|
* <p><code>Template template = new Template("I like {{FOOD}}.");<br>
|
|
* TemplateDictionary dictionary = new TemplateDictionary();<br>
|
|
* dictionary.put("food", "cookies");<br>
|
|
* String result = template.parse(dictionary);</code>
|
|
* <p>Value of result : <code>I like cookies.</code>
|
|
*/
|
|
public class Template {
|
|
private static final Pattern pattern = Pattern.compile(
|
|
"\\{\\{(#|/){0,1}(\\w+)\\}\\}", Pattern.CANON_EQ
|
|
);
|
|
|
|
private TemplateNode parsedTemplate[];
|
|
private String originalTemplate;
|
|
|
|
|
|
/**
|
|
* Instantiate and compile a new template with the template provided.
|
|
* @param template String containing the template.
|
|
*/
|
|
public Template(String template) throws Exception
|
|
{
|
|
this.originalTemplate = template;
|
|
this.parsedTemplate = initTemplate(template);
|
|
}
|
|
|
|
|
|
/**
|
|
* Instantiate and compile a new template with the template provided.
|
|
* @param file File containing the template.
|
|
*/
|
|
public Template(File file) throws Exception
|
|
{
|
|
if(file == null || !file.canRead()){
|
|
throw new Exception("Cannot read "+file.getName());
|
|
}
|
|
|
|
FileReader reader = new FileReader(file);
|
|
StringBuilder b = new StringBuilder();
|
|
|
|
char buf[] = new char[1024];
|
|
for(int i=0; (i = reader.read(buf)) != -1;){
|
|
b.append(buf, 0, i);
|
|
}
|
|
|
|
reader.close();
|
|
this.originalTemplate = b.toString();
|
|
this.parsedTemplate = initTemplate(this.originalTemplate);
|
|
}
|
|
|
|
|
|
private static TemplateNode[] initTemplate(String template) throws Exception
|
|
{
|
|
ArrayList<TemplateNode> parsedTemplate = new ArrayList<TemplateNode>();
|
|
Stack<String> sections = new Stack<String>();
|
|
Matcher match = pattern.matcher(template);
|
|
int lastpos = 0;
|
|
|
|
while(match.find()){
|
|
String segment = template.substring(lastpos,match.start());
|
|
if(segment.length() > 0){
|
|
parsedTemplate.add(new TemplateNode(TemplateNode.PLAIN, segment));
|
|
}
|
|
|
|
if("#".equals(match.group(1))){
|
|
sections.push(match.group(2));
|
|
parsedTemplate.add(
|
|
new TemplateNode(TemplateNode.SECTION_START, match.group(2))
|
|
);
|
|
} else if("/".equals(match.group(1))){
|
|
parsedTemplate.add(
|
|
new TemplateNode(TemplateNode.SECTION_END, match.group(2))
|
|
);
|
|
if(sections.empty() || !sections.pop().equals(match.group(2))){
|
|
throw new Exception("Out of turn section termation at "+match.start());
|
|
}
|
|
} else {
|
|
parsedTemplate.add(
|
|
new TemplateNode(TemplateNode.VARIABLE, match.group(2))
|
|
);
|
|
}
|
|
lastpos = match.end();
|
|
}
|
|
|
|
String segment = template.substring(lastpos, template.length());
|
|
if(segment.length() > 0){
|
|
parsedTemplate.add(new TemplateNode(TemplateNode.PLAIN, segment));
|
|
}
|
|
|
|
if(!sections.empty()){
|
|
throw new Exception("Unterminated section in template");
|
|
}
|
|
|
|
return parsedTemplate.toArray(new TemplateNode[0]);
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse this template with the provided dictionary and output it to a string.
|
|
* <p><b>This method is threadsafe.</b>
|
|
* @param dict Template dictionary to parse against.
|
|
* @return String containing the parsed result of the template.
|
|
*/
|
|
public String parse(TemplateDictionary dict)
|
|
{
|
|
StringBuilder b = new StringBuilder();
|
|
|
|
if(dict != null){ parseTemplate(dict, b, parsedTemplate, 0); }
|
|
else { parseTemplate(b, parsedTemplate); }
|
|
|
|
return b.toString();
|
|
}
|
|
|
|
|
|
private static int parseTemplate(StringBuilder output, TemplateNode template[])
|
|
{
|
|
for(int i = 0; i < template.length; i++){
|
|
if(template[i].getNodeType() == TemplateNode.PLAIN){
|
|
output.append(template[i].getContent());
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
private static int parseTemplate(TemplateDictionary dict, StringBuilder output, TemplateNode template[], int i)
|
|
{
|
|
for(;i < template.length; i++){
|
|
switch(template[i].getNodeType()){
|
|
case TemplateNode.PLAIN:
|
|
output.append(template[i].getContent());
|
|
break;
|
|
|
|
case TemplateNode.VARIABLE:
|
|
output.append(dict.get(template[i].getContent()));
|
|
break;
|
|
|
|
case TemplateNode.SECTION_START:
|
|
if(dict.isShown(template[i].getContent())){
|
|
List<TemplateDictionary> l = (
|
|
dict.getParent() == null ? dict.getChild(template[i].getContent()) :
|
|
dict.getParent().getChild(template[i].getContent())
|
|
);
|
|
|
|
if(l == null){
|
|
i = parseTemplate(dict, output, template, i+1);
|
|
} else {
|
|
int last = 0;
|
|
for(TemplateDictionary d : l){
|
|
last = parseTemplate(d, output, template, i+1);
|
|
}
|
|
i = last;
|
|
}
|
|
} else {
|
|
for(int c=1; c > 0;){
|
|
i++;
|
|
switch(template[i].getNodeType()){
|
|
case TemplateNode.SECTION_START: c++; break;
|
|
case TemplateNode.SECTION_END: c--; break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TemplateNode.SECTION_END: return i;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
}
|