|
1 /* |
|
2 * Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 * Parameter tokenizer is used to parse function parameters lists |
|
17 * |
|
18 */ |
|
19 package com.nokia.tracebuilder.source; |
|
20 |
|
21 import java.util.List; |
|
22 |
|
23 /** |
|
24 * Parameter tokenizer is used to parse function parameters lists |
|
25 * |
|
26 */ |
|
27 public class SourceParameterTokenizer { |
|
28 |
|
29 /** |
|
30 * The source parser |
|
31 */ |
|
32 private SourceParser parser; |
|
33 |
|
34 /** |
|
35 * The offset where to start the tokenizer |
|
36 */ |
|
37 private int offset; |
|
38 |
|
39 /** |
|
40 * Slip next whitespace |
|
41 */ |
|
42 private boolean skipNextWhiteSpace; |
|
43 |
|
44 /** |
|
45 * Constructor |
|
46 * |
|
47 * @param parser |
|
48 * the source parser |
|
49 * @param offset |
|
50 * offset to the start of parameter |
|
51 */ |
|
52 public SourceParameterTokenizer(SourceParser parser, int offset) { |
|
53 this.parser = parser; |
|
54 this.offset = offset; |
|
55 } |
|
56 |
|
57 /** |
|
58 * Parses a list of parameters (a, b, c) and stores the values into the |
|
59 * given list. |
|
60 * |
|
61 * @param list |
|
62 * the list of parameters |
|
63 * @param findSeparator |
|
64 * if true, the processing stops after ';' or '{' character. If |
|
65 * false, processing stops after ')' at end of parameters |
|
66 * @return index at end of parameters |
|
67 * @throws SourceParserException |
|
68 * if processing fails |
|
69 */ |
|
70 public int tokenize(List<String> list, boolean findSeparator) |
|
71 throws SourceParserException { |
|
72 TokenizerSearchData data = new TokenizerSearchData(); |
|
73 data.itr = parser.createIterator(offset, SourceParser.SKIP_WHITE_SPACES |
|
74 | SourceParser.SKIP_COMMENTS); |
|
75 boolean finished = false; |
|
76 while (data.itr.hasNext() && !finished) { |
|
77 data.value = data.itr.next(); |
|
78 if (!data.inQuotes && data.value == '\"') { |
|
79 data.inQuotes = true; |
|
80 data.hasData = true; |
|
81 } else if (data.inQuotes) { |
|
82 processInQuotesChar(data); |
|
83 } else if (data.complete) { |
|
84 processEndOfParametersChar(data); |
|
85 } else if (data.value == '(') { |
|
86 processOpeningBracket(data); |
|
87 } else if (data.value == ',' || data.value == ')') { |
|
88 processCommaOrClosingBracket(list, data); |
|
89 } else if (data.value == ';' || data.value == '{' |
|
90 || data.value == '}') { |
|
91 throw new SourceParserException( |
|
92 SourceErrorCodes.UNEXPECTED_PARAMETER_SEPARATOR); |
|
93 } else { |
|
94 // Raises a flag that there is some data. processOpeningBracket |
|
95 // no longer interprets the next bracket as opening bracket |
|
96 if (data.openBracketCount > 0) { |
|
97 data.hasData = true; |
|
98 } |
|
99 } |
|
100 finished = ((data.complete && !findSeparator) || (data.endFound && findSeparator)); |
|
101 } |
|
102 if (!data.complete) { |
|
103 throw new SourceParserException( |
|
104 SourceErrorCodes.UNEXPECTED_END_OF_FILE); |
|
105 } |
|
106 if (data.openBracketCount != 0) { |
|
107 throw new SourceParserException(SourceErrorCodes.BRACKET_MISMATCH); |
|
108 } |
|
109 return data.itr.currentIndex() + 1; |
|
110 } |
|
111 |
|
112 /** |
|
113 * Parses list of parameters with types (int a, int b, int c) and stores the |
|
114 * values into the given list. |
|
115 * |
|
116 * @param list |
|
117 * the list of parameters |
|
118 * @return index at end of parameters |
|
119 * @throws SourceParserException |
|
120 * if processing fails |
|
121 */ |
|
122 public int tokenizeTyped(List<SourceParameter> list) |
|
123 throws SourceParserException { |
|
124 TokenizerSearchData data = new TokenizerSearchData(); |
|
125 |
|
126 try { |
|
127 data.itr = parser |
|
128 .createIterator(offset, SourceParser.SKIP_WHITE_SPACES |
|
129 | SourceParser.SKIP_COMMENTS); |
|
130 data.sourceParameter = new SourceParameter(); |
|
131 while (data.itr.hasNext() && !data.complete) { |
|
132 data.value = data.itr.next(); |
|
133 |
|
134 // Check if there was array start or end character and then |
|
135 // space. It would mean that the parameter continues and more |
|
136 // should be parsed. |
|
137 if (skipNextWhiteSpace) { |
|
138 skipNextWhiteSpace = false; |
|
139 if (data.itr.hasSkipped()) { |
|
140 data.value = data.itr.next(); |
|
141 } |
|
142 } |
|
143 |
|
144 if (data.value == '\"') { |
|
145 throw new SourceParserException( |
|
146 SourceErrorCodes.UNEXPECTED_QUOTE_CHARACTER); |
|
147 } else if (data.value == '(') { |
|
148 processOpeningBracket(data); |
|
149 } else if (data.value == ',' || data.value == ')') { |
|
150 processCommaOrClosingBracket(list, data); |
|
151 } else if (data.value == ';' || data.value == '{' |
|
152 || data.value == '}') { |
|
153 throw new SourceParserException( |
|
154 SourceErrorCodes.UNEXPECTED_PARAMETER_SEPARATOR); |
|
155 |
|
156 // Array start or end character. |
|
157 } else if (data.value == '<' || data.value == '>') { |
|
158 skipNextWhiteSpace = true; |
|
159 } else if (data.itr.hasSkipped()) { |
|
160 processNameValueSeparator(data); |
|
161 } |
|
162 } |
|
163 if (!data.complete) { |
|
164 throw new SourceParserException( |
|
165 SourceErrorCodes.UNEXPECTED_END_OF_FILE); |
|
166 } |
|
167 } catch (SourceParserException e) { |
|
168 // Resets all source locations if parser fails |
|
169 for (int i = 0; i < list.size(); i++) { |
|
170 list.get(i).getSourceLocation().dereference(); |
|
171 } |
|
172 throw e; |
|
173 } |
|
174 return data.itr.currentIndex() + 1; |
|
175 } |
|
176 |
|
177 /** |
|
178 * Processes a separator character and updates the current SourceParameter |
|
179 * object in the search data |
|
180 * |
|
181 * @param data |
|
182 * the search data |
|
183 * @throws SourceParserException |
|
184 * if processing fails |
|
185 */ |
|
186 private void processNameValueSeparator(TokenizerSearchData data) |
|
187 throws SourceParserException { |
|
188 // If the parameter is empty, the previous index will point |
|
189 // to index preceeding tagStartIndex |
|
190 int previous = data.itr.previousIndex(); |
|
191 if (previous >= data.tagStartIndex) { |
|
192 int endIndex = previous + 1; |
|
193 if (data.sourceParameter.getType() == null) { |
|
194 processNameValueSeparatorNoType(data, endIndex); |
|
195 } else if (data.sourceParameter.getName() == null) { |
|
196 processNameValueSeparatorNoName(data, endIndex); |
|
197 } |
|
198 data.tagStartIndex = data.itr.currentIndex(); |
|
199 } |
|
200 } |
|
201 |
|
202 /** |
|
203 * Processes a name-value separator when there is no name |
|
204 * |
|
205 * @param data |
|
206 * the search data |
|
207 * @param endIndex |
|
208 * the end index of the parameters |
|
209 * @throws SourceParserException |
|
210 * if processing fails |
|
211 */ |
|
212 private void processNameValueSeparatorNoName(TokenizerSearchData data, |
|
213 int endIndex) throws SourceParserException { |
|
214 String name = parser.getSource().get(data.tagStartIndex, |
|
215 endIndex - data.tagStartIndex); |
|
216 boolean startFound = false; |
|
217 int start = 0; |
|
218 int end = name.length(); |
|
219 for (int i = 0; i < name.length(); i++) { |
|
220 char c = name.charAt(i); |
|
221 if (c == '&' || c == '*') { |
|
222 if (c == '&') { |
|
223 data.sourceParameter.setReference(); |
|
224 } else { |
|
225 data.sourceParameter.addPointer(); |
|
226 } |
|
227 if (!startFound) { |
|
228 start++; |
|
229 } else { |
|
230 end--; |
|
231 } |
|
232 } else { |
|
233 startFound = true; |
|
234 } |
|
235 } |
|
236 name = name.substring(start, end); |
|
237 if (name.length() > 0) { |
|
238 if (isParameterTypeQualifier(name)) { |
|
239 // Qualifiers between type and name are ignored |
|
240 // For example TInt const* aValue |
|
241 } else { |
|
242 data.sourceParameter.setName(name); |
|
243 } |
|
244 } |
|
245 } |
|
246 |
|
247 /** |
|
248 * Processes a name-value separator when there is no value |
|
249 * |
|
250 * @param data |
|
251 * the search data |
|
252 * @param endIndex |
|
253 * the end index of the parameters |
|
254 * @throws SourceParserException |
|
255 * if processing fails |
|
256 */ |
|
257 private void processNameValueSeparatorNoType(TokenizerSearchData data, |
|
258 int endIndex) throws SourceParserException { |
|
259 String type = parser.getSource().get(data.tagStartIndex, |
|
260 endIndex - data.tagStartIndex); |
|
261 if (isParameterTypeQualifier(type)) { |
|
262 data.sourceParameter.addQualifier(type); |
|
263 } else { |
|
264 for (int i = type.length() - 1; i >= 0; i--) { |
|
265 if (type.charAt(i) == '&') { |
|
266 data.sourceParameter.setReference(); |
|
267 if (i == 0) { |
|
268 type = ""; //$NON-NLS-1$ |
|
269 } |
|
270 } else if (type.charAt(i) == '*') { |
|
271 data.sourceParameter.addPointer(); |
|
272 if (i == 0) { |
|
273 type = ""; //$NON-NLS-1$ |
|
274 } |
|
275 } else { |
|
276 if (i != type.length() - 1) { |
|
277 type = type.substring(0, i + 1); |
|
278 } |
|
279 i = -1; |
|
280 } |
|
281 } |
|
282 if (type.length() > 0) { |
|
283 // Remove spaces |
|
284 type = type.replace(" ", ""); //$NON-NLS-1$ //$NON-NLS-2$ |
|
285 data.sourceParameter.setType(type); |
|
286 } |
|
287 } |
|
288 } |
|
289 |
|
290 /** |
|
291 * Checks if parameter type if a qualifier or not |
|
292 * |
|
293 * @param type |
|
294 * the type to be checked |
|
295 * @return true if qualifier, false if not |
|
296 */ |
|
297 private boolean isParameterTypeQualifier(String type) { |
|
298 boolean retval = false; |
|
299 for (String element : SourceConstants.PARAMETER_QUALIFIERS) { |
|
300 if (type.equals(element)) { |
|
301 retval = true; |
|
302 } |
|
303 } |
|
304 return retval; |
|
305 } |
|
306 |
|
307 /** |
|
308 * Processes a parameter separator or closing bracket |
|
309 * |
|
310 * @param list |
|
311 * the list of existing parameters |
|
312 * @param data |
|
313 * the search data |
|
314 * @throws SourceParserException |
|
315 * if invalid character is encountered |
|
316 */ |
|
317 @SuppressWarnings("unchecked") |
|
318 private void processCommaOrClosingBracket(List list, |
|
319 TokenizerSearchData data) throws SourceParserException { |
|
320 // This method is called from both tokenize functions. One uses |
|
321 // List<String> and other List<SourceParameter> |
|
322 // Thus this uses List and @SuppressWarnings |
|
323 if (data.openBracketCount == data.initialBracketCount) { |
|
324 if (data.sourceParameter != null) { |
|
325 // If processing typed parameter list, the name and type are |
|
326 // stored into a SourceParameter object, which is then added |
|
327 // to list |
|
328 processNameValueSeparator(data); |
|
329 if (data.sourceParameter.getType() != null) { |
|
330 SourceLocation location = new SourceLocation(parser, |
|
331 data.paramStartIndex, data.itr.currentIndex() |
|
332 - data.paramStartIndex); |
|
333 data.sourceParameter.setSourceLocation(location); |
|
334 list.add(data.sourceParameter); |
|
335 data.sourceParameter = new SourceParameter(); |
|
336 } |
|
337 } else { |
|
338 // In this case the list contains strings. |
|
339 int previous = data.itr.previousIndex(); |
|
340 if (previous >= data.tagStartIndex) { |
|
341 int endIndex = data.itr.previousIndex() + 1; |
|
342 list.add(parser.getSource().get(data.tagStartIndex, |
|
343 endIndex - data.tagStartIndex)); |
|
344 } else { |
|
345 list.add(""); //$NON-NLS-1$ |
|
346 } |
|
347 } |
|
348 if (data.value == ')') { |
|
349 data.complete = true; |
|
350 data.openBracketCount--; |
|
351 } else { |
|
352 data.tagStartIndex = data.itr.nextIndex(); |
|
353 data.paramStartIndex = data.tagStartIndex; |
|
354 } |
|
355 } else if (data.openBracketCount > data.initialBracketCount) { |
|
356 if (data.value == ')') { |
|
357 data.openBracketCount--; |
|
358 } |
|
359 } else { |
|
360 if (data.value == ')') { |
|
361 throw new SourceParserException( |
|
362 SourceErrorCodes.BRACKET_MISMATCH); |
|
363 } |
|
364 throw new SourceParserException( |
|
365 SourceErrorCodes.UNEXPECTED_PARAMETER_SEPARATOR); |
|
366 } |
|
367 } |
|
368 |
|
369 /** |
|
370 * Processes an opening bracket |
|
371 * |
|
372 * @param data |
|
373 * the search data |
|
374 */ |
|
375 private void processOpeningBracket(TokenizerSearchData data) { |
|
376 data.openBracketCount++; |
|
377 if (!data.hasData) { |
|
378 // The number of initial '(' characters is stored. The |
|
379 // parameters are assumed to end when the corresponding ')' |
|
380 // character is encountered |
|
381 data.tagStartIndex = data.itr.nextIndex(); |
|
382 data.paramStartIndex = data.tagStartIndex; |
|
383 data.initialBracketCount = data.openBracketCount; |
|
384 } |
|
385 } |
|
386 |
|
387 /** |
|
388 * Process a character when in quotes |
|
389 * |
|
390 * @param data |
|
391 * the search data |
|
392 */ |
|
393 private void processInQuotesChar(TokenizerSearchData data) { |
|
394 if (data.value == '\"' && data.previousValue != '\\') { |
|
395 data.inQuotes = false; |
|
396 } |
|
397 data.previousValue = data.value; |
|
398 } |
|
399 |
|
400 /** |
|
401 * Processes a character found after the closing bracket |
|
402 * |
|
403 * @param data |
|
404 * the data |
|
405 * @throws SourceParserException |
|
406 * if invalid characters are found |
|
407 */ |
|
408 private void processEndOfParametersChar(TokenizerSearchData data) |
|
409 throws SourceParserException { |
|
410 if (data.value == ';' || data.value == '{') { |
|
411 data.endFound = true; |
|
412 } else if (data.value == ')') { |
|
413 data.openBracketCount--; |
|
414 } else if (!Character.isWhitespace(data.value)) { |
|
415 throw new SourceParserException(SourceErrorCodes.BRACKET_MISMATCH); |
|
416 } |
|
417 } |
|
418 |
|
419 } |