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><logana:converters></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 }