001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.tools.template_engine;
003
004import java.util.Arrays;
005import java.util.List;
006
007public 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}