package play.templates;
import java.util.Stack;
import play.Logger;
import play.vfs.VirtualFile;
import play.exceptions.TemplateCompilationException;
import play.exceptions.PlayException;
import play.exceptions.UnexpectedException;
public abstract class TemplateCompiler {
public BaseTemplate compile(BaseTemplate template) {
try {
long start = System.currentTimeMillis();
generate(template);
if (Logger.isTraceEnabled()) {
Logger.trace("%sms to parse template %s", System.currentTimeMillis() - start, template.name);
}
return template;
} catch (PlayException e) {
throw e;
} catch (Exception e) {
throw new UnexpectedException(e);
}
}
public BaseTemplate compile(VirtualFile file) {
return compile(new GroovyTemplate(file.relativePath(), file.contentAsString()));
}
StringBuilder compiledSource = new StringBuilder();
BaseTemplate template;
TemplateParser parser;
boolean doNextScan = true;
TemplateParser.Token state;
Stack<Tag> tagsStack = new Stack<Tag>();
int tagIndex;
boolean skipLineBreak;
int currentLine = 1;
static class Tag {
String name;
int startLine;
boolean hasBody;
}
void generate(BaseTemplate template) {
this.template = template;
String source = source();
this.parser = new TemplateParser(source);
// Class header
head();
// Parse
loop:
for (;;) {
if (doNextScan) {
state = parser.nextToken();
} else {
doNextScan = true;
}
switch (state) {
case EOF:
break loop;
case PLAIN:
plain();
break;
case SCRIPT:
script();
break;
case EXPR:
expr();
break;
case MESSAGE:
message();
break;
case ACTION:
action(false);
break;
case ABS_ACTION:
action(true);
break;
case COMMENT:
skipLineBreak = true;
break;
case START_TAG:
startTag();
break;
case END_TAG:
endTag();
break;
}
}
// Class end
end();
// Check tags imbrication
if (!tagsStack.empty()) {
Tag tag = tagsStack.peek();
throw new TemplateCompilationException(template, tag.startLine, "#{" + tag.name + "} is not closed.");
}
// Done !
template.compiledSource = compiledSource.toString();
if (Logger.isTraceEnabled()) {
Logger.trace("%s is compiled to %s", template.name, template.compiledSource);
}
}
abstract String source();
abstract void head();
abstract void end();
abstract void plain();
abstract void script();
abstract void expr();
abstract void message();
abstract void action(boolean absolute);
abstract void startTag();
abstract void endTag();
void markLine(int line) {
compiledSource.append("// line ").append(line);
template.linesMatrix.put(currentLine, line);
}
void println() {
compiledSource.append("\n");
currentLine++;
}
void print(String text) {
compiledSource.append(text);
}
void println(String text) {
compiledSource.append(text);
println();
}
}