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 }