001 // License: GPL. For details, see LICENSE file.
002 package org.openstreetmap.josm.tools.template_engine;
003
004
005 import static org.openstreetmap.josm.tools.I18n.tr;
006
007 import java.util.ArrayList;
008 import java.util.Arrays;
009 import java.util.Collection;
010 import java.util.List;
011
012 import org.openstreetmap.josm.actions.search.SearchCompiler;
013 import org.openstreetmap.josm.actions.search.SearchCompiler.Match;
014 import org.openstreetmap.josm.tools.template_engine.Tokenizer.Token;
015 import org.openstreetmap.josm.tools.template_engine.Tokenizer.TokenType;
016
017
018 public class TemplateParser {
019 private final Tokenizer tokenizer;
020
021 private static final Collection<TokenType> EXPRESSION_END_TOKENS = Arrays.asList(TokenType.EOF);
022 private static final Collection<TokenType> CONDITION_WITH_APOSTROPHES_END_TOKENS = Arrays.asList(TokenType.APOSTROPHE);
023
024 public TemplateParser(String template) {
025 this.tokenizer = new Tokenizer(template);
026 }
027
028 private Token check(TokenType expectedToken) throws ParseError {
029 Token token = tokenizer.nextToken();
030 if (token.getType() != expectedToken)
031 throw new ParseError(token, expectedToken);
032 else
033 return token;
034 }
035
036 public TemplateEntry parse() throws ParseError {
037 return parseExpression(EXPRESSION_END_TOKENS);
038 }
039
040 private TemplateEntry parseExpression(Collection<TokenType> endTokens) throws ParseError {
041 List<TemplateEntry> entries = new ArrayList<TemplateEntry>();
042 while (true) {
043 TemplateEntry templateEntry;
044 Token token = tokenizer.lookAhead();
045 if (token.getType() == TokenType.CONDITION_START) {
046 templateEntry = parseCondition();
047 } else if (token.getType() == TokenType.CONTEXT_SWITCH_START) {
048 templateEntry = parseContextSwitch();
049 } else if (token.getType() == TokenType.VARIABLE_START) {
050 templateEntry = parseVariable();
051 } else if (endTokens.contains(token.getType()))
052 return CompoundTemplateEntry.fromArray(entries.toArray(new TemplateEntry[entries.size()]));
053 else if (token.getType() == TokenType.TEXT) {
054 tokenizer.nextToken();
055 templateEntry = new StaticText(token.getText());
056 } else
057 throw new ParseError(token);
058 entries.add(templateEntry);
059 }
060 }
061
062 private TemplateEntry parseVariable() throws ParseError {
063 check(TokenType.VARIABLE_START);
064 String variableName = check(TokenType.TEXT).getText();
065 check(TokenType.END);
066
067 return new Variable(variableName);
068 }
069
070 private void skipWhitespace() throws ParseError {
071 Token token = tokenizer.lookAhead();
072 if (token.getType() == TokenType.TEXT && token.getText().trim().isEmpty()) {
073 tokenizer.nextToken();
074 }
075 }
076
077 private TemplateEntry parseCondition() throws ParseError {
078 check(TokenType.CONDITION_START);
079 Condition result = new Condition();
080 while (true) {
081
082 TemplateEntry condition;
083 Token searchExpression = tokenizer.skip('\'');
084 check(TokenType.APOSTROPHE);
085 condition = parseExpression(CONDITION_WITH_APOSTROPHES_END_TOKENS);
086 check(TokenType.APOSTROPHE);
087 if (searchExpression.getText().trim().isEmpty()) {
088 result.getEntries().add(condition);
089 } else {
090 try {
091 result.getEntries().add(new SearchExpressionCondition(SearchCompiler.compile(searchExpression.getText(), false, false), condition));
092 } catch (org.openstreetmap.josm.actions.search.SearchCompiler.ParseError e) {
093 throw new ParseError(searchExpression.getPosition(), e);
094 }
095 }
096 skipWhitespace();
097
098 Token token = tokenizer.lookAhead();
099 if (token.getType() == TokenType.END) {
100 tokenizer.nextToken();
101 return result;
102 } else {
103 check(TokenType.PIPE);
104 }
105 }
106 }
107
108 private TemplateEntry parseContextSwitch() throws ParseError {
109
110 check(TokenType.CONTEXT_SWITCH_START);
111 Token searchExpression = tokenizer.skip('\'');
112 check(TokenType.APOSTROPHE);
113 TemplateEntry template = parseExpression(CONDITION_WITH_APOSTROPHES_END_TOKENS);
114 check(TokenType.APOSTROPHE);
115 ContextSwitchTemplate result;
116 if (searchExpression.getText().trim().isEmpty())
117 throw new ParseError(tr("Expected search expression"));
118 else {
119 try {
120 Match match = SearchCompiler.compile(searchExpression.getText(), false, false);
121 result = new ContextSwitchTemplate(match, template, searchExpression.getPosition());
122 } catch (org.openstreetmap.josm.actions.search.SearchCompiler.ParseError e) {
123 throw new ParseError(searchExpression.getPosition(), e);
124 }
125 }
126 skipWhitespace();
127 check(TokenType.END);
128 return result;
129 }
130
131 }