001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.core.subst;
015
016import ch.qos.logback.core.CoreConstants;
017import ch.qos.logback.core.spi.ScanException;
018
019import java.util.ArrayList;
020import java.util.List;
021
022public class Tokenizer {
023
024    enum TokenizerState {
025        LITERAL_STATE, START_STATE, DEFAULT_VAL_STATE
026    }
027
028    final String pattern;
029    final int patternLength;
030
031    public Tokenizer(String pattern) {
032        this.pattern = pattern;
033        patternLength = pattern.length();
034    }
035
036    TokenizerState state = TokenizerState.LITERAL_STATE;
037    int pointer = 0;
038
039    List<Token> tokenize() throws ScanException {
040        List<Token> tokenList = new ArrayList<Token>();
041        StringBuilder buf = new StringBuilder();
042
043        while (pointer < patternLength) {
044            char c = pattern.charAt(pointer);
045            pointer++;
046
047            switch (state) {
048            case LITERAL_STATE:
049                handleLiteralState(c, tokenList, buf);
050                break;
051            case START_STATE:
052                handleStartState(c, tokenList, buf);
053                break;
054            case DEFAULT_VAL_STATE:
055                handleDefaultValueState(c, tokenList, buf);
056            default:
057            }
058        }
059        // EOS
060        switch (state) {
061        case LITERAL_STATE:
062            addLiteralToken(tokenList, buf);
063            break;
064        case DEFAULT_VAL_STATE:
065            // trailing colon. see also LOGBACK-1140
066            buf.append(CoreConstants.COLON_CHAR);
067            addLiteralToken(tokenList, buf);
068            break;
069        case START_STATE:
070            // trailing $. see also LOGBACK-1149
071            buf.append(CoreConstants.DOLLAR);
072            addLiteralToken(tokenList, buf);
073            break;
074        }
075        return tokenList;
076    }
077
078    private void handleDefaultValueState(char c, List<Token> tokenList, StringBuilder stringBuilder) {
079        switch (c) {
080        case CoreConstants.DASH_CHAR:
081            tokenList.add(Token.DEFAULT_SEP_TOKEN);
082            state = TokenizerState.LITERAL_STATE;
083            break;
084        case CoreConstants.DOLLAR:
085            stringBuilder.append(CoreConstants.COLON_CHAR);
086            addLiteralToken(tokenList, stringBuilder);
087            stringBuilder.setLength(0);
088            state = TokenizerState.START_STATE;
089            break;
090        case CoreConstants.CURLY_LEFT:
091            stringBuilder.append(CoreConstants.COLON_CHAR);
092            addLiteralToken(tokenList, stringBuilder);
093            stringBuilder.setLength(0);
094            tokenList.add(Token.CURLY_LEFT_TOKEN);
095            state = TokenizerState.LITERAL_STATE;
096
097            break;
098        default:
099            stringBuilder.append(CoreConstants.COLON_CHAR).append(c);
100            state = TokenizerState.LITERAL_STATE;
101            break;
102        }
103    }
104
105    private void handleStartState(char c, List<Token> tokenList, StringBuilder stringBuilder) {
106        if (c == CoreConstants.CURLY_LEFT) {
107            tokenList.add(Token.START_TOKEN);
108        } else {
109            stringBuilder.append(CoreConstants.DOLLAR).append(c);
110        }
111        state = TokenizerState.LITERAL_STATE;
112    }
113
114    private void handleLiteralState(char c, List<Token> tokenList, StringBuilder stringBuilder) {
115        switch (c) {
116        case CoreConstants.DOLLAR:
117            addLiteralToken(tokenList, stringBuilder);
118            stringBuilder.setLength(0);
119            state = TokenizerState.START_STATE;
120            break;
121        case CoreConstants.COLON_CHAR:
122            addLiteralToken(tokenList, stringBuilder);
123            stringBuilder.setLength(0);
124            state = TokenizerState.DEFAULT_VAL_STATE;
125            break;
126        case CoreConstants.CURLY_LEFT:
127            addLiteralToken(tokenList, stringBuilder);
128            tokenList.add(Token.CURLY_LEFT_TOKEN);
129            stringBuilder.setLength(0);
130            break;
131        case CoreConstants.CURLY_RIGHT:
132            addLiteralToken(tokenList, stringBuilder);
133            tokenList.add(Token.CURLY_RIGHT_TOKEN);
134            stringBuilder.setLength(0);
135            break;
136        default:
137            stringBuilder.append(c);
138        }
139
140    }
141
142    private void addLiteralToken(List<Token> tokenList, StringBuilder stringBuilder) {
143        if (stringBuilder.length() == 0)
144            return;
145        tokenList.add(new Token(Token.Type.LITERAL, stringBuilder.toString()));
146    }
147
148}