/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ratpack.groovy.template.internal;
import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
import java.io.IOException;
/**
* Note: hardcoded to expect UTF-8.
*/
public class TextTemplateParser {
private static byte[] bytes(String s) {
return s.getBytes(CharsetUtil.UTF_8);
}
private static final byte[] LESS_THAN = bytes("<");
private static final byte[] DOLLAR_BRACE = bytes("${");
private static final byte[] PERCENT = bytes("%");
private static final byte[] START_OUTPUT = bytes("$(\n\"\"\"");
private static final byte[] START_CODE = bytes("\"\"\"\n);");
private static final byte[] CLOSE_BRACE = bytes("}");
private static final byte[] DOLLAR = bytes("$");
private static final byte[] BACKSLASH = bytes("\\");
private static final byte[] SEMICOLON = bytes(";");
public void parse(ByteBuf input, ByteBuf output) throws IOException {
startScript(output);
byte c;
while (input.isReadable()) {
c = input.readByte();
if (c == '<') {
input.markReaderIndex();
c = input.readByte();
if (c != '%') {
output.writeBytes(LESS_THAN);
input.resetReaderIndex();
} else {
input.markReaderIndex();
c = input.readByte();
if (c == '=') {
groovyExpression(input, output);
} else {
input.resetReaderIndex();
groovySection(input, output);
}
}
continue; // at least '<' is consumed ... read next chars.
}
if (c == '$') {
input.markReaderIndex();
c = input.readByte();
if (c != '{') {
output.writeBytes(DOLLAR);
input.resetReaderIndex();
} else {
input.markReaderIndex();
processGSstring(input, output);
}
continue; // at least '$' is consumed ... read next chars.
}
if (c == '\"') {
output.writeBytes(BACKSLASH);
}
// Handle raw new line characters.
if (c == '\n' || c == '\r') {
if (c == '\r') { // on Windows, "\r\n" is a new line.
input.markReaderIndex();
c = input.readByte();
if (c != '\n') {
input.resetReaderIndex();
}
}
output.writeByte('\n');
continue;
}
output.writeByte(c);
}
endScript(output);
}
private void startScript(ByteBuf output) {
output.writeBytes(START_OUTPUT);
}
private void endScript(ByteBuf output) {
output.writeBytes(START_CODE);
}
private void processGSstring(ByteBuf input, ByteBuf output) throws IOException {
output.writeBytes(DOLLAR_BRACE);
byte c;
while (input.isReadable()) {
c = input.readByte();
if (c != '\n' && c != '\r') {
output.writeByte(c);
}
if (c == '}') {
break;
}
}
}
private void groovyExpression(ByteBuf input, ByteBuf output) throws IOException {
output.writeBytes(DOLLAR_BRACE);
byte c;
while (input.isReadable()) {
c = input.readByte();
if (c == '%') {
c = input.readByte();
if (c != '>') {
output.writeBytes(PERCENT);
} else {
break;
}
}
if (c != '\n' && c != '\r') {
output.writeByte(c);
}
}
output.writeBytes(CLOSE_BRACE);
}
private void groovySection(ByteBuf input, ByteBuf output) throws IOException {
output.writeBytes(START_CODE);
output.writeByte('\n');
byte c;
while (input.isReadable()) {
c = input.readByte();
if (c == '%') {
c = input.readByte();
if (c != '>') {
output.writeBytes(PERCENT);
} else {
break;
}
}
output.writeByte(c);
}
output.writeByte('\n');
output.writeBytes(SEMICOLON);
output.writeBytes(START_OUTPUT);
}
}