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    }