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 }