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.pattern.parser; 015 016import java.util.ArrayList; 017import java.util.List; 018 019import ch.qos.logback.core.pattern.util.AsIsEscapeUtil; 020import ch.qos.logback.core.pattern.util.IEscapeUtil; 021 022import static ch.qos.logback.core.CoreConstants.CURLY_RIGHT; 023 024import static ch.qos.logback.core.CoreConstants.ESCAPE_CHAR; 025import static ch.qos.logback.core.CoreConstants.COMMA_CHAR; 026import static ch.qos.logback.core.CoreConstants.SINGLE_QUOTE_CHAR; 027import static ch.qos.logback.core.CoreConstants.DOUBLE_QUOTE_CHAR; 028 029import ch.qos.logback.core.pattern.parser.TokenStream.TokenizerState; 030import ch.qos.logback.core.spi.ScanException; 031 032public class OptionTokenizer { 033 034 private final static int EXPECTING_STATE = 0; 035 private final static int RAW_COLLECTING_STATE = 1; 036 private final static int QUOTED_COLLECTING_STATE = 2; 037 038 final IEscapeUtil escapeUtil; 039 final TokenStream tokenStream; 040 final String pattern; 041 final int patternLength; 042 043 char quoteChar; 044 int state = EXPECTING_STATE; 045 046 OptionTokenizer(TokenStream tokenStream) { 047 this(tokenStream, new AsIsEscapeUtil()); 048 } 049 050 OptionTokenizer(TokenStream tokenStream, IEscapeUtil escapeUtil) { 051 this.tokenStream = tokenStream; 052 this.pattern = tokenStream.pattern; 053 this.patternLength = tokenStream.patternLength; 054 this.escapeUtil = escapeUtil; 055 } 056 057 void tokenize(char firstChar, List<Token> tokenList) throws ScanException { 058 StringBuffer buf = new StringBuffer(); 059 List<String> optionList = new ArrayList<String>(); 060 char c = firstChar; 061 062 while (tokenStream.pointer < patternLength) { 063 switch (state) { 064 case EXPECTING_STATE: 065 switch (c) { 066 case ' ': 067 case '\t': 068 case '\r': 069 case '\n': 070 case COMMA_CHAR: 071 break; 072 case SINGLE_QUOTE_CHAR: 073 case DOUBLE_QUOTE_CHAR: 074 state = QUOTED_COLLECTING_STATE; 075 quoteChar = c; 076 break; 077 case CURLY_RIGHT: 078 emitOptionToken(tokenList, optionList); 079 return; 080 default: 081 buf.append(c); 082 state = RAW_COLLECTING_STATE; 083 } 084 break; 085 case RAW_COLLECTING_STATE: 086 switch (c) { 087 case COMMA_CHAR: 088 optionList.add(buf.toString().trim()); 089 buf.setLength(0); 090 state = EXPECTING_STATE; 091 break; 092 case CURLY_RIGHT: 093 optionList.add(buf.toString().trim()); 094 emitOptionToken(tokenList, optionList); 095 return; 096 default: 097 buf.append(c); 098 } 099 break; 100 case QUOTED_COLLECTING_STATE: 101 if (c == quoteChar) { 102 optionList.add(buf.toString()); 103 buf.setLength(0); 104 state = EXPECTING_STATE; 105 } else if (c == ESCAPE_CHAR) { 106 escape(String.valueOf(quoteChar), buf); 107 } else { 108 buf.append(c); 109 } 110 111 break; 112 } 113 114 c = pattern.charAt(tokenStream.pointer); 115 tokenStream.pointer++; 116 } 117 118 // EOS 119 if (c == CURLY_RIGHT) { 120 if (state == EXPECTING_STATE) { 121 emitOptionToken(tokenList, optionList); 122 } else if (state == RAW_COLLECTING_STATE) { 123 optionList.add(buf.toString().trim()); 124 emitOptionToken(tokenList, optionList); 125 } else { 126 throw new ScanException("Unexpected end of pattern string in OptionTokenizer"); 127 } 128 } else { 129 throw new ScanException("Unexpected end of pattern string in OptionTokenizer"); 130 } 131 } 132 133 void emitOptionToken(List<Token> tokenList, List<String> optionList) { 134 tokenList.add(new Token(Token.OPTION, optionList)); 135 tokenStream.state = TokenizerState.LITERAL_STATE; 136 } 137 138 void escape(String escapeChars, StringBuffer buf) { 139 if ((tokenStream.pointer < patternLength)) { 140 char next = pattern.charAt(tokenStream.pointer++); 141 escapeUtil.escape(escapeChars, buf, next, tokenStream.pointer); 142 } 143 } 144}