View Javadoc
1   package net.logAnalyzer.patternParser;
2   
3   import java.util.Vector;
4   
5   import net.logAnalyzer.config.ConfigurationManager;
6   import net.logAnalyzer.converters.LAConverter;
7   import net.logAnalyzer.converters.NewLineConverter;
8   
9   /***
10   * This class parses a log4j PatternLayout pattern.
11   * <p>
12   * The pattern could contain log4j basic converters, but also custom converters.
13   * </p>
14   * <p>
15   * Each pattern is declared in the <tt>&lt;logana:converters&gt;</tt> section
16   * of the configuration file loaded by
17   * {@link net.logAnalyzer.config.ConfigurationManager}.
18   * </p>
19   * 
20   * @author Karim REFEYTON
21   * @version 0.1
22   */
23  public class PatternParser {
24      protected static final char CONVERTER_PREFIX = '%';
25  
26      protected static final char OPENED_BRAKET = '{';
27  
28      protected static final char CLOSED_BRAKET = '}';
29  
30      private int linesToRead = 1;
31  
32      /***
33       * Pattern to analyse.
34       */
35      private String pattern = null;
36  
37      /***
38       * Default constructor.
39       * 
40       * @param pattern
41       *            Pattern to analysis.
42       */
43      public PatternParser(String pattern) {
44          this.pattern = pattern;
45      }
46  
47      static final int STATE_LOOKING = 0;
48      static final int STATE_CONVERTER = 1;
49      static final int STATE_OPENBRAKET = 2;
50      static final int STATE_OPTION = 3;
51      /***
52       * Parses the pattern char by char. The pattern is transformed as a column
53       * list.
54       */
55      public LAConverter[] parse() throws PatternParsingException {
56          // Converters
57          Vector converters = new Vector();
58          // Current converter
59          LAConverter converter = null;
60          // Buffer following current position
61          StringBuffer buffer = new StringBuffer();
62          // Buffer for literals
63          StringBuffer literal = new StringBuffer();
64          // Buffer for converter
65          String letter = null;
66          // Buffer for options
67          StringBuffer option = new StringBuffer();
68          // Defines parsing states
69          int state = STATE_LOOKING;
70          // Parses pattern
71          int patternLength = pattern.length();
72          for (int i = 0; i < patternLength; i++) {
73              // Reads the current character
74              char c = pattern.charAt(i);
75              switch (state) {
76              case STATE_LOOKING:
77                  // Looking for '%' or literal
78                  if (c == CONVERTER_PREFIX) {
79                      // Creates the buffer
80                      buffer.append(c);
81                      // Continues to the next state
82                      state = STATE_CONVERTER;
83                  } else {
84                      // Extends the current literal
85                      literal.append(c);
86                  }
87                  break;
88              case STATE_CONVERTER:
89                  // Reading converter char
90                  if (c == CONVERTER_PREFIX) {
91                      // Extends the current literal with the escaped special
92                      // character CONVERTER_PREFIX
93                      literal.append(c);
94                      // Returns to the first state
95                      buffer.setLength(0);
96                      state = STATE_LOOKING;
97                  } else {
98                      // Saves the converter letter
99                      letter = Character.toString(c);
100                     // Extends the buffer
101                     buffer.append(c);
102                     // Continues to the next state
103                     state = STATE_OPENBRAKET;
104                 }
105                 break;
106             case STATE_OPENBRAKET:
107                 // Looking for "{" or literal
108                 if (c == '{') {
109                     // Extends the buffer
110                     buffer.append(c);
111                     // Continues to the next state
112                     state = STATE_OPTION;
113                 } else {
114                     try {
115                         // Creates the converter without option
116                         converter = ConfigurationManager.getInstance()
117                                 .createConverter(letter);
118                         // Adds the literal if not empty
119                         addLiteral(converters, literal.toString());
120                         // Adds the converter
121                         addConverter(converters, converter);
122                         // Unparses the current character
123                         i--;
124                         // Continues to the next state
125                         letter = null;
126                         literal.setLength(0);
127                         buffer.setLength(0);
128                         state = STATE_LOOKING;
129                     } catch (PatternParsingException ppe) {
130                         // The converter is unknown so its a literal
131                         // Extends the current literal
132                         literal.append(buffer);
133                         buffer.setLength(0);
134                         // Unparses the current character
135                         i--;
136                         // Returns to the first state
137                         state = STATE_LOOKING;
138                     }
139                 }
140                 break;
141             case STATE_OPTION:
142                 // Looking for '}' or option
143                 if (c == CLOSED_BRAKET) {
144                     try {
145                         // Creates the converter with its option
146                         converter = ConfigurationManager.getInstance()
147                                 .createConverter(letter, option.toString());
148                         // Adds the literal if not empty
149                         addLiteral(converters, literal.toString());
150                         // Adds the converter
151                         addConverter(converters, converter);
152                         // Continues to the next state
153                         letter = null;
154                         literal.setLength(0);
155                         buffer.setLength(0);
156                         option.setLength(0);
157                         state = STATE_LOOKING;
158                     } catch (PatternParsingException ppe) {
159                         // The converter is unknown so its a literal
160                         // Extends the current literal
161                         literal.append(buffer).append(CLOSED_BRAKET);
162                         // Returns to the first state
163                         buffer.setLength(0);
164                         option.setLength(0);
165                         state = STATE_LOOKING;
166                     }
167                 } else if (c == CONVERTER_PREFIX && i < patternLength - 1
168                         && pattern.charAt(i + 1) != CONVERTER_PREFIX) {
169                     // There is no option but a literal beginning with the
170                     // opened braket and terminated closed to a converter
171                     try {
172                         // Creates the converter without option
173                         converter = ConfigurationManager.getInstance()
174                                 .createConverter(letter);
175                         // Adds the literal if not empty
176                         addLiteral(converters, literal.toString());
177                         // Adds the converter
178                         addConverter(converters, converter);
179                         // Unparses the current character
180                         i--;
181                         // Continues to the next state
182                         letter = null;
183                         literal.setLength(0);
184                         literal.append(buffer);
185                         buffer.setLength(0);
186                         option.setLength(0);
187                         state = STATE_LOOKING;
188                     } catch (PatternParsingException ppe) {
189                         // The converter is unknown so its a literal
190                         // Extends the current literal
191                         literal.append(buffer);
192                         // Unparses the current character
193                         i--;
194                         // Returns to the first state
195                         buffer.setLength(0);
196                         option.setLength(0);
197                         state = STATE_LOOKING;
198                     }
199                 } else if (c == CONVERTER_PREFIX && i < patternLength - 1
200                         && pattern.charAt(i + 1) == CONVERTER_PREFIX) {
201                     // Extends the current literal with the escaped special
202                     // character CONVERTER_PREFIX
203                     literal.append(CONVERTER_PREFIX);
204                     // Returns to the first state
205                     buffer.setLength(0);
206                     option.setLength(0);
207                     state = STATE_LOOKING;
208                 } else {
209                     // Extends the current option
210                     option.append(c);
211                     buffer.append(c);
212                 }
213                 break;
214              default:
215             }
216         }
217         // Adds the ending literal if not empty
218         addLiteral(converters, literal.toString());
219 
220         // Controls if literal and non literal converters are alternated
221         // boolean expectLiteral = true;
222         // for (int i = 0; i < converters.size(); i++) {
223         // LAConverter converter = (LAConverter) converters.get(i);
224         // if (i > 0
225         // && ((converter.isLiteral() && !expectLiteral) || (!converter
226         // .isLiteral() && expectLiteral))) {
227         // // throws new Parse
228         // }
229         // // expectLiteral
230         // }
231 
232         LAConverter[] conv = new LAConverter[converters.size()];
233         return (LAConverter[]) converters.toArray(conv);
234     }
235 
236     /***
237      * Constructs a converter from the converterChar parsed in the pattern.
238      * 
239      * @param converters
240      *            Converters.
241      * @param converter
242      *            LAConverter to add in the converters list.
243      */
244     private void addConverter(Vector converters, LAConverter converter) {
245         if (converters.size() > 0) {
246             LAConverter previous = (LAConverter) converters.get(converters
247                     .size() - 1);
248             previous.setNext(converter);
249         }
250         converters.add(converter);
251         if (converter instanceof NewLineConverter) {
252             linesToRead++;
253         }
254     }
255 
256     /***
257      * Constructs a literal converter to handle the last parsed string.
258      * 
259      * @param converters
260      *            Converters.
261      * @param literal
262      *            Literal parsed in the pattern.
263      * @throws PatternParsingException
264      *             If can't create a literal converter.
265      */
266     private void addLiteral(Vector converters, String literal)
267             throws PatternParsingException {
268         if (literal != null && literal.length() > 0) {
269             LAConverter converter = ConfigurationManager.getInstance()
270                     .createLiteralConverter(literal);
271             if (converters.size() > 0) {
272                 LAConverter previous = (LAConverter) converters.get(converters
273                         .size() - 1);
274                 previous.setNext(converter);
275             }
276             converters.add(converter);
277         }
278     }
279 
280     /***
281      * Returns the number of lines to read from the log file to parse a log
282      * messageLabel. For example, if a log4j pattern contains a %n converter inside
283      * (not at the end), the number of lines is 2.
284      * 
285      * @return The number of line to read in a log file.
286      */
287     public int getLinesToRead() {
288         return linesToRead;
289     }
290 }