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    }