001 // License: GPL. For details, see LICENSE file.
002 package org.openstreetmap.josm.tools.template_engine;
003
004 import java.util.Arrays;
005 import java.util.List;
006
007 public class Tokenizer {
008
009 public static class Token {
010 private final TokenType type;
011 private final int position;
012 private final String text;
013
014 public Token(TokenType type, int position) {
015 this(type, position, null);
016 }
017
018 public Token(TokenType type, int position, String text) {
019 this.type = type;
020 this.position = position;
021 this.text = text;
022 }
023
024 public TokenType getType() {
025 return type;
026 }
027
028 public int getPosition() {
029 return position;
030 }
031
032 public String getText() {
033 return text;
034 }
035
036 @Override
037 public String toString() {
038 return type + (text != null?" " + text:"");
039 }
040 }
041
042 public enum TokenType { CONDITION_START, VARIABLE_START, CONTEXT_SWITCH_START, END, PIPE, APOSTROPHE, TEXT, EOF }
043
044 private final List<Character> specialCharaters = Arrays.asList(new Character[] {'$', '?', '{', '}', '|', '\'', '!'});
045
046 private final String template;
047
048 private int c;
049 private int index;
050 private Token currentToken;
051 private StringBuilder text = new StringBuilder();
052
053 public Tokenizer(String template) {
054 this.template = template;
055 getChar();
056 }
057
058 private void getChar() {
059 if (index >= template.length()) {
060 c = -1;
061 } else {
062 c = template.charAt(index++);
063 }
064 }
065
066 public Token nextToken() throws ParseError {
067 if (currentToken != null) {
068 Token result = currentToken;
069 currentToken = null;
070 return result;
071 }
072 int position = index;
073
074 text.setLength(0);
075 switch (c) {
076 case -1:
077 return new Token(TokenType.EOF, position);
078 case '{':
079 getChar();
080 return new Token(TokenType.VARIABLE_START, position);
081 case '?':
082 getChar();
083 if (c == '{') {
084 getChar();
085 return new Token(TokenType.CONDITION_START, position);
086 } else
087 throw ParseError.unexpectedChar('{', (char)c, position);
088 case '!':
089 getChar();
090 if (c == '{') {
091 getChar();
092 return new Token(TokenType.CONTEXT_SWITCH_START, position);
093 } else
094 throw ParseError.unexpectedChar('{', (char)c, position);
095 case '}':
096 getChar();
097 return new Token(TokenType.END, position);
098 case '|':
099 getChar();
100 return new Token(TokenType.PIPE, position);
101 case '\'':
102 getChar();
103 return new Token(TokenType.APOSTROPHE, position);
104 default:
105 while (c != -1 && !specialCharaters.contains((char)c)) {
106 if (c == '\\') {
107 getChar();
108 if (c == 'n') {
109 c = '\n';
110 }
111 }
112 text.append((char)c);
113 getChar();
114 }
115 return new Token(TokenType.TEXT, position, text.toString());
116 }
117 }
118
119 public Token lookAhead() throws ParseError {
120 if (currentToken == null) {
121 currentToken = nextToken();
122 }
123 return currentToken;
124 }
125
126 public Token skip(char lastChar) {
127 currentToken = null;
128 int position = index;
129 StringBuilder result = new StringBuilder();
130 while (c != lastChar && c != -1) {
131 if (c == '\\') {
132 getChar();
133 }
134 result.append((char)c);
135 getChar();
136 }
137 return new Token(TokenType.TEXT, position, result.toString());
138 }
139
140 }