org.javalite.templator.Template Maven / Gradle / Ivy
The newest version!
package org.javalite.templator;
import org.javalite.templator.tags.IfTag;
import org.javalite.templator.tags.ListTag;
import java.io.Writer;
import java.util.*;
import static org.javalite.common.Util.split;
/**
* @author Igor Polevoy on 1/10/15.
*/
public class Template {
private final List templateTokens = new ArrayList();
public Template(String template) {
try{
parse(template);
}catch(ParseException e){
throw e;
}catch(Exception e){
throw new TemplateException(e);
}
}
//for testing only
List templateTokens() {
return templateTokens;
}
private void parse(String template) throws IllegalAccessException, InstantiationException {
Map tags = TemplatorConfig.instance().getTags();
Stack stack = new Stack();
List tagsList = new ArrayList();
//TODO: separate iterations through all tags, and focus on stack
for (int templateIndex = 0; templateIndex < template.length(); templateIndex++) {
for (String tagName : tags.keySet()) {
Class tagClass = tags.get(tagName);
AbstractTag tag; // ony used to test things
tag = (AbstractTag) tagClass.newInstance();
//match tag start, such as "<#list"
if (stack.isEmpty() && tag.matchStartAtIndex(template, templateIndex)) {
tag.setTagStartIndex(templateIndex - tag.getTagStart().length());
stack.push(tag);
continue;
}
//match tag argument end, such as: <#list people as person > body #list>
// ---^
if (!stack.isEmpty()
&& stack.peek().getArgumentsEndIndex() == -1
&& stack.peek().marchArgumentEnd(template, templateIndex)) {
AbstractTag currentTag = stack.peek();
int argumentsStartIndex = currentTag.getTagStartIndex() + currentTag.getTagStart().length();
int argumentsEndIndex = currentTag.getArgumentsEndIndex();
String arguments = template.substring(argumentsStartIndex, argumentsEndIndex);
currentTag.setArguments(arguments);
}
//match tag end, such as: <#list people as person > body #list>
// ---^
if (!stack.isEmpty() && stack.peek().matchEndTag(template, templateIndex)) {
AbstractTag currentTag = stack.pop();
tagsList.add(currentTag);
String arguments;
if (currentTag.getArgumentsEndIndex() != -1) { // we have body. case like this: <#list people as person > body #list>
arguments = template.substring(currentTag.getTagStartIndex() + currentTag.getTagStart().length(), currentTag.getArgumentsEndIndex());
String body = template.substring(currentTag.getArgumentsEndIndex() + 1, currentTag.getTagEndIndex());
currentTag.setBody(body);
} else {
// this is what is between the start and end tag in case there is no body:
///<@blah arguments for blah />
arguments = template.substring(currentTag.getTagStartIndex() + currentTag.getTagStart().length(), currentTag.getTagEndIndex());
}
currentTag.setArguments(arguments);
}
}
}
if(tagsList.size() == 0){ // assuming that this is just text, no funny business
templateTokens.add(new StringToken(template));
}else{
//now we need to collect string chunks in between
for (int i = 0; i < tagsList.size(); i++) {
AbstractTag currentTag = tagsList.get(i);
// if this is a first tag and not at beginning of template
if (i == 0 && currentTag.getTagStartIndex() != 0) {
templateTokens.add(new StringToken(template.substring(0, currentTag.getTagStartIndex())));
}
// if there is a gap between current and previous
if(tagsList.size() > 1 && i != 0){
int startIndex = tagsList.get(i - 1).getTagEndIndex() + 1;
int endIndex = currentTag.getTagStartIndex();
StringToken st = new StringToken(template.substring(startIndex, endIndex ));
templateTokens.add(st);
}
templateTokens.add(currentTag);
// if this is the last tag and there is some text after
if(i == (tagsList.size() - 1) && template.length() > currentTag.getTagEndIndex()) {
StringToken st = new StringToken(template.substring(currentTag.getTagEndIndex() + currentTag.getMatchingEnd().length()));
templateTokens.add(st);
}
}
}
if (!stack.isEmpty()) {
throw new ParseException("At least one tag is not closed: " + stack.peek().getTagName());
}
}
private static class Token {
int index;
String token;
private Token(int index, String token) {
this.index = index;
this.token = token;
}
}
public void process(Map values, Writer writer) {
try {
for (TemplateToken token : templateTokens) {
token.process(values, writer);
}
} catch (Exception e) {
throw new TemplateException(e);
}
}
}