View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core.pattern.parser;
15  
16  import java.util.ArrayList;
17  import java.util.List;
18  
19  import ch.qos.logback.core.pattern.util.AsIsEscapeUtil;
20  import ch.qos.logback.core.pattern.util.IEscapeUtil;
21  
22  import static ch.qos.logback.core.CoreConstants.CURLY_RIGHT;
23  
24  import static ch.qos.logback.core.CoreConstants.ESCAPE_CHAR;
25  import static ch.qos.logback.core.CoreConstants.COMMA_CHAR;
26  import static ch.qos.logback.core.CoreConstants.SINGLE_QUOTE_CHAR;
27  import static ch.qos.logback.core.CoreConstants.DOUBLE_QUOTE_CHAR;
28  
29  import ch.qos.logback.core.pattern.parser.TokenStream.TokenizerState;
30  import ch.qos.logback.core.spi.ScanException;
31  
32  public class OptionTokenizer {
33  
34      private final static int EXPECTING_STATE = 0;
35      private final static int RAW_COLLECTING_STATE = 1;
36      private final static int QUOTED_COLLECTING_STATE = 2;
37  
38      final IEscapeUtil escapeUtil;
39      final TokenStream tokenStream;
40      final String pattern;
41      final int patternLength;
42  
43      char quoteChar;
44      int state = EXPECTING_STATE;
45  
46      OptionTokenizer(TokenStream tokenStream) {
47          this(tokenStream, new AsIsEscapeUtil());
48      }
49  
50      OptionTokenizer(TokenStream tokenStream, IEscapeUtil escapeUtil) {
51          this.tokenStream = tokenStream;
52          this.pattern = tokenStream.pattern;
53          this.patternLength = tokenStream.patternLength;
54          this.escapeUtil = escapeUtil;
55      }
56  
57      void tokenize(char firstChar, List<Token> tokenList) throws ScanException {
58          StringBuffer buf = new StringBuffer();
59          List<String> optionList = new ArrayList<String>();
60          char c = firstChar;
61  
62          while (tokenStream.pointer < patternLength) {
63              switch (state) {
64              case EXPECTING_STATE:
65                  switch (c) {
66                  case ' ':
67                  case '\t':
68                  case '\r':
69                  case '\n':
70                  case COMMA_CHAR:
71                      break;
72                  case SINGLE_QUOTE_CHAR:
73                  case DOUBLE_QUOTE_CHAR:
74                      state = QUOTED_COLLECTING_STATE;
75                      quoteChar = c;
76                      break;
77                  case CURLY_RIGHT:
78                      emitOptionToken(tokenList, optionList);
79                      return;
80                  default:
81                      buf.append(c);
82                      state = RAW_COLLECTING_STATE;
83                  }
84                  break;
85              case RAW_COLLECTING_STATE:
86                  switch (c) {
87                  case COMMA_CHAR:
88                      optionList.add(buf.toString().trim());
89                      buf.setLength(0);
90                      state = EXPECTING_STATE;
91                      break;
92                  case CURLY_RIGHT:
93                      optionList.add(buf.toString().trim());
94                      emitOptionToken(tokenList, optionList);
95                      return;
96                  default:
97                      buf.append(c);
98                  }
99                  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 }