// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.tools.template_engine;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.openstreetmap.josm.actions.search.SearchCompiler;
import org.openstreetmap.josm.actions.search.SearchCompiler.Match;
import org.openstreetmap.josm.tools.template_engine.Tokenizer.Token;
import org.openstreetmap.josm.tools.template_engine.Tokenizer.TokenType;
public class TemplateParser {
private final Tokenizer tokenizer;
private static final Collection<TokenType> EXPRESSION_END_TOKENS = Arrays.asList(TokenType.EOF);
private static final Collection<TokenType> CONDITION_WITH_APOSTROPHES_END_TOKENS = Arrays.asList(TokenType.APOSTROPHE);
public TemplateParser(String template) {
this.tokenizer = new Tokenizer(template);
}
private Token check(TokenType expectedToken) throws ParseError {
Token token = tokenizer.nextToken();
if (token.getType() != expectedToken)
throw new ParseError(token, expectedToken);
else
return token;
}
public TemplateEntry parse() throws ParseError {
return parseExpression(EXPRESSION_END_TOKENS);
}
private TemplateEntry parseExpression(Collection<TokenType> endTokens) throws ParseError {
List<TemplateEntry> entries = new ArrayList<>();
while (true) {
TemplateEntry templateEntry;
Token token = tokenizer.lookAhead();
if (token.getType() == TokenType.CONDITION_START) {
templateEntry = parseCondition();
} else if (token.getType() == TokenType.CONTEXT_SWITCH_START) {
templateEntry = parseContextSwitch();
} else if (token.getType() == TokenType.VARIABLE_START) {
templateEntry = parseVariable();
} else if (endTokens.contains(token.getType()))
return CompoundTemplateEntry.fromArray(entries.toArray(new TemplateEntry[entries.size()]));
else if (token.getType() == TokenType.TEXT) {
tokenizer.nextToken();
templateEntry = new StaticText(token.getText());
} else
throw new ParseError(token);
entries.add(templateEntry);
}
}
private TemplateEntry parseVariable() throws ParseError {
check(TokenType.VARIABLE_START);
String variableName = check(TokenType.TEXT).getText();
check(TokenType.END);
return new Variable(variableName);
}
private void skipWhitespace() throws ParseError {
Token token = tokenizer.lookAhead();
if (token.getType() == TokenType.TEXT && token.getText().trim().isEmpty()) {
tokenizer.nextToken();
}
}
private TemplateEntry parseCondition() throws ParseError {
check(TokenType.CONDITION_START);
Condition result = new Condition();
while (true) {
TemplateEntry condition;
Token searchExpression = tokenizer.skip('\'');
check(TokenType.APOSTROPHE);
condition = parseExpression(CONDITION_WITH_APOSTROPHES_END_TOKENS);
check(TokenType.APOSTROPHE);
if (searchExpression.getText().trim().isEmpty()) {
result.getEntries().add(condition);
} else {
try {
result.getEntries().add(new SearchExpressionCondition(SearchCompiler.compile(searchExpression.getText(), false, false), condition));
} catch (org.openstreetmap.josm.actions.search.SearchCompiler.ParseError e) {
throw new ParseError(searchExpression.getPosition(), e);
}
}
skipWhitespace();
Token token = tokenizer.lookAhead();
if (token.getType() == TokenType.END) {
tokenizer.nextToken();
return result;
} else {
check(TokenType.PIPE);
}
}
}
private TemplateEntry parseContextSwitch() throws ParseError {
check(TokenType.CONTEXT_SWITCH_START);
Token searchExpression = tokenizer.skip('\'');
check(TokenType.APOSTROPHE);
TemplateEntry template = parseExpression(CONDITION_WITH_APOSTROPHES_END_TOKENS);
check(TokenType.APOSTROPHE);
ContextSwitchTemplate result;
if (searchExpression.getText().trim().isEmpty())
throw new ParseError(tr("Expected search expression"));
else {
try {
Match match = SearchCompiler.compile(searchExpression.getText(), false, false);
result = new ContextSwitchTemplate(match, template, searchExpression.getPosition());
} catch (org.openstreetmap.josm.actions.search.SearchCompiler.ParseError e) {
throw new ParseError(searchExpression.getPosition(), e);
}
}
skipWhitespace();
check(TokenType.END);
return result;
}
}