package org.jtwig.parser.parboiled;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import org.apache.commons.lang3.StringUtils;
import org.jtwig.environment.Environment;
import org.jtwig.model.tree.Node;
import org.jtwig.parser.JtwigParser;
import org.jtwig.parser.ParseException;
import org.jtwig.parser.config.JtwigParserConfiguration;
import org.jtwig.parser.util.ParboiledExceptionMessageExtractor;
import org.jtwig.resource.ResourceService;
import org.jtwig.resource.metadata.ResourceMetadata;
import org.jtwig.resource.reference.ResourceReference;
import org.parboiled.errors.ParseError;
import org.parboiled.errors.ParserRuntimeException;
import org.parboiled.parserunners.BasicParseRunner;
import org.parboiled.support.ParsingResult;
import java.nio.charset.Charset;
import java.util.List;
import static org.parboiled.common.FileUtils.readAllText;
public class ParboiledJtwigParser implements JtwigParser {
private final ParboiledExceptionMessageExtractor exceptionMessageExtractor = new ParboiledExceptionMessageExtractor();
private final JtwigParserConfiguration configuration;
public ParboiledJtwigParser(JtwigParserConfiguration configuration) {
this.configuration = configuration;
}
@Override
public Node parse(Environment environment, ResourceReference resource) {
ResourceService resourceService = environment.getResourceEnvironment().getResourceService();
BasicParseRunner<Node> runner = new BasicParseRunner<Node>(
ParserContext.instance(resource, configuration,
configuration.getAddonParserProviders(),
configuration.getUnaryOperators(),
configuration.getBinaryOperators(),
configuration.getTestExpressionParsers())
.parser(DocumentParser.class)
.NodeRule()
);
try {
ResourceMetadata resourceMetadata = resourceService.loadMetadata(resource);
Charset charset = resourceMetadata.getCharset()
.or(environment.getResourceEnvironment().getDefaultInputCharset());
ParsingResult<Node> result = runner.run(readAllText(resourceMetadata.load(), charset));
if (result.hasErrors()) {
throw new ParseException(toMessage(result.parseErrors));
} else if (!result.matched) {
throw new ParseException("Invalid template format");
} else {
return result.valueStack.pop();
}
} catch (ParserRuntimeException e) {
if ((e.getCause() != null) && (e.getCause() instanceof ParseException)) {
ParseException cause = (ParseException) e.getCause();
throw new ParseException(String.format("%s\n%s", cause.getMessage(), exceptionMessageExtractor.extract(e)), cause.getCause());
} else {
throw new ParseException(e);
}
}
}
private String toMessage(List<ParseError> parseErrors) {
return StringUtils.join(Collections2.transform(parseErrors, new Function<ParseError, String>() {
@Override
public String apply(ParseError input) {
return String.format("%d, %d -> %s", input.getStartIndex(), input.getEndIndex(), input.getErrorMessage());
}
}), "\n");
}
}