|
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 * Parser for source contexts |
|
17 * |
|
18 */ |
|
19 package com.nokia.tracecompiler.source; |
|
20 |
|
21 import java.util.ArrayList; |
|
22 import java.util.Collections; |
|
23 import java.util.Iterator; |
|
24 import java.util.List; |
|
25 |
|
26 /** |
|
27 * Parser for source contexts |
|
28 * |
|
29 */ |
|
30 class ContextAreaParser { |
|
31 |
|
32 /** |
|
33 * Source parser |
|
34 */ |
|
35 private SourceParser parser; |
|
36 |
|
37 /** |
|
38 * List of source file contexts |
|
39 */ |
|
40 protected ArrayList<SourceContext> contextAreas = new ArrayList<SourceContext>(); |
|
41 |
|
42 /** |
|
43 * Comparator for array sorting and searching |
|
44 */ |
|
45 private PositionArrayComparator arrayComparator = new PositionArrayComparator(); |
|
46 |
|
47 /** |
|
48 * "usingnamespace" text |
|
49 */ |
|
50 private static final String USINGNAMESPACE = "usingnamespace"; //$NON-NLS-1$ |
|
51 |
|
52 /** |
|
53 * Start index of "using" substring in "usingnamespace" string |
|
54 */ |
|
55 private static final int START_INDEX_OF_USING_SUBSTRING = 0; // CodForChk_Dis_Magic |
|
56 |
|
57 /** |
|
58 * End index of "using" substring in "usingnamespace" string |
|
59 */ |
|
60 private static final int END_INDEX_OF_USING_SUBSTRING = 5; // CodForChk_Dis_Magic |
|
61 |
|
62 /** |
|
63 * Start index of "namespace" substring in "usingnamespace" string |
|
64 */ |
|
65 private static final int START_INDEX_OF_NAMESPACE_SUBSTRING = 5; // CodForChk_Dis_Magic |
|
66 |
|
67 /** |
|
68 * End index of "namespace" substring in "usingnamespace" string |
|
69 */ |
|
70 private static final int END_INDEX_OF_NAMESPACE_SUBSTRING = 14; // CodForChk_Dis_Magic |
|
71 |
|
72 /** |
|
73 * Constructor |
|
74 * |
|
75 * @param parser |
|
76 * the source parser |
|
77 */ |
|
78 ContextAreaParser(SourceParser parser) { |
|
79 this.parser = parser; |
|
80 } |
|
81 |
|
82 /** |
|
83 * Resets the context areas |
|
84 */ |
|
85 void reset() { |
|
86 contextAreas.clear(); |
|
87 } |
|
88 |
|
89 /** |
|
90 * Returns the context at given offset |
|
91 * |
|
92 * @param offset |
|
93 * the offset to the source data |
|
94 * @return the context at the offset or null if no context exists |
|
95 * @throws SourceParserException |
|
96 * if parser fails |
|
97 */ |
|
98 SourceContext parseAndGet(int offset) throws SourceParserException { |
|
99 if (contextAreas.isEmpty()) { |
|
100 parseAll(); |
|
101 } |
|
102 int index = find(offset); |
|
103 SourceContext context = null; |
|
104 if (index >= 0) { |
|
105 context = contextAreas.get(index); |
|
106 } |
|
107 return context; |
|
108 } |
|
109 |
|
110 /** |
|
111 * Gets the context areas. If the areas have not been parsed, this parses |
|
112 * them |
|
113 * |
|
114 * @return the areas |
|
115 * @throws SourceParserException |
|
116 * if parser fails |
|
117 */ |
|
118 Iterator<SourceContext> parseAndGetAll() throws SourceParserException { |
|
119 if (contextAreas.isEmpty()) { |
|
120 parseAll(); |
|
121 } |
|
122 return contextAreas.iterator(); |
|
123 } |
|
124 |
|
125 /** |
|
126 * Gets the context area list. This does not parse the areas |
|
127 * |
|
128 * @return the list of context areas |
|
129 */ |
|
130 List<SourceContext> getContextList() { |
|
131 return contextAreas; |
|
132 } |
|
133 |
|
134 /** |
|
135 * Finds the array index of the context area which contains the offset. If |
|
136 * none of the areas contain the offset, returns negative integer indicating |
|
137 * the index of the context area following the offset |
|
138 * |
|
139 * @param offset |
|
140 * the offset to the data |
|
141 * @return the context area index |
|
142 */ |
|
143 int find(int offset) { |
|
144 return Collections.binarySearch(contextAreas, new SourceLocationBase( |
|
145 parser, offset), arrayComparator); |
|
146 } |
|
147 |
|
148 /** |
|
149 * Builds the context array |
|
150 * |
|
151 * @throws SourceParserException |
|
152 * if parser fails |
|
153 */ |
|
154 void parseAll() throws SourceParserException { // CodForChk_Dis_ComplexFunc |
|
155 contextAreas.clear(); |
|
156 char value; |
|
157 |
|
158 int inBrackets = 0; |
|
159 int inContext = 0; |
|
160 int inNamespace = 0; |
|
161 |
|
162 int usingIndex = START_INDEX_OF_USING_SUBSTRING; |
|
163 int usingKeywordEnd = 0; |
|
164 int namespaceIndex = START_INDEX_OF_NAMESPACE_SUBSTRING; |
|
165 int nameSpaceKeywordEnd = 0; |
|
166 int previousIndexBeforeNamespace = 0; |
|
167 boolean checkNextCharacter = false; |
|
168 |
|
169 SourceContext context = null; |
|
170 SourceIterator itr = parser.createIterator(0, SourceParser.SKIP_ALL); |
|
171 |
|
172 while (itr.hasNext()) { |
|
173 value = itr.next(); |
|
174 |
|
175 // Next character check is need only if we have found "namespace" |
|
176 // text |
|
177 if (checkNextCharacter) { |
|
178 |
|
179 // Next character after "namespace" text should be space. |
|
180 // Because we have skipped spaces, current index should be |
|
181 // bigger than nameSpaceKeywordEnd + 1. If it is not space then |
|
182 // we are not inside namespace |
|
183 if (itr.currentIndex() - nameSpaceKeywordEnd < 2) { // CodForChk_Dis_Magic |
|
184 inNamespace--; |
|
185 } |
|
186 checkNextCharacter = false; |
|
187 } |
|
188 |
|
189 // Check is character part of "using" text |
|
190 if (value == USINGNAMESPACE.charAt(usingIndex)) { |
|
191 usingIndex++; |
|
192 } else { |
|
193 |
|
194 // Character not part of "using" text -> reset usingIndex |
|
195 usingIndex = START_INDEX_OF_USING_SUBSTRING; |
|
196 } |
|
197 |
|
198 // Check that did we found "using" text |
|
199 if (usingIndex == END_INDEX_OF_USING_SUBSTRING) { |
|
200 usingKeywordEnd = itr.currentIndex(); |
|
201 usingIndex = START_INDEX_OF_USING_SUBSTRING; |
|
202 } |
|
203 |
|
204 // Check is character part of "namespace" text |
|
205 if (value == USINGNAMESPACE.charAt(namespaceIndex)) { |
|
206 if (previousIndexBeforeNamespace == 0) { |
|
207 previousIndexBeforeNamespace = itr.previousIndex(); |
|
208 } |
|
209 namespaceIndex++; |
|
210 } else { |
|
211 |
|
212 // Character not part of "namespace" text -> reset |
|
213 // previousIndexBeforeNamespace and namespaceIndex |
|
214 previousIndexBeforeNamespace = 0; |
|
215 namespaceIndex = START_INDEX_OF_NAMESPACE_SUBSTRING; |
|
216 } |
|
217 |
|
218 // Check that did we found "namespace" text |
|
219 if (namespaceIndex == END_INDEX_OF_NAMESPACE_SUBSTRING) { |
|
220 nameSpaceKeywordEnd = itr.currentIndex(); |
|
221 |
|
222 // If there was "using" text just before "namespace" text, then |
|
223 // namespace is defined like: "using namespace foo;" and we are |
|
224 // not going inside namespace brackets |
|
225 if (usingKeywordEnd != previousIndexBeforeNamespace) { |
|
226 inNamespace++; |
|
227 checkNextCharacter = true; |
|
228 } |
|
229 namespaceIndex = START_INDEX_OF_NAMESPACE_SUBSTRING; |
|
230 } |
|
231 |
|
232 if (value == '{') { |
|
233 inBrackets++; |
|
234 |
|
235 // Check that are we inside namespace or context |
|
236 if (inBrackets > inNamespace) { |
|
237 inContext++; |
|
238 if (inContext == 1) { |
|
239 int start = itr.currentIndex() + 1; |
|
240 context = new SourceContext(parser, start); |
|
241 |
|
242 // Includes the '{' character into the context |
|
243 if (!createContext(context, start - 2)) { // CodForChk_Dis_Magic |
|
244 context = null; |
|
245 } |
|
246 } |
|
247 } |
|
248 } else if (value == '}') { |
|
249 // Check that are we exiting from context or namespace |
|
250 if (inBrackets == inNamespace) { |
|
251 inNamespace--; |
|
252 } else { |
|
253 inContext--; |
|
254 if (inContext == 0 && context != null) { |
|
255 context.setLength(itr.currentIndex() + 1 |
|
256 - context.getOffset()); |
|
257 contextAreas.add(context); |
|
258 } |
|
259 } |
|
260 |
|
261 inBrackets--; |
|
262 } |
|
263 } |
|
264 } |
|
265 |
|
266 /** |
|
267 * Sets the data to the source context |
|
268 * |
|
269 * @param context |
|
270 * the source context to be updated |
|
271 * @param offset |
|
272 * the index preceeding the '{' character |
|
273 * @return true if valid, false otherwise |
|
274 * @throws SourceParserException |
|
275 * if processing fails |
|
276 */ |
|
277 private boolean createContext(SourceContext context, int offset) |
|
278 throws SourceParserException { |
|
279 ContextSearchData data = new ContextSearchData(); |
|
280 data.itr = parser.createIterator(offset, SourceParser.BACKWARD_SEARCH |
|
281 | SourceParser.SKIP_ALL); |
|
282 data.context = context; |
|
283 while (data.itr.hasNext() && !data.finished) { |
|
284 char c = data.itr.next(); |
|
285 // Function start or stop character or statement separator breaks |
|
286 // the search in normal case. In case of nested class separator |
|
287 // character breaks the search. |
|
288 if (c == ';' || c == '}' || c == '{' |
|
289 || (c == ':' && data.itr.peek() == ':') |
|
290 && data.classStartIndex != -1) { |
|
291 processContextTerminator(context, data, false); |
|
292 } else if (!data.parametersFound) { |
|
293 processParametersNotFoundCharacter(data, c); |
|
294 } else if (c == ')' || c == '(' || c == ',' |
|
295 || (c == ':' && data.itr.peek() != ':')) { |
|
296 // Constructor member initializer list may contain brackets, ',' |
|
297 // and ':'. When one of the characters from member initializer |
|
298 // list is encountered, this assumes that the previous |
|
299 // one was not the actual function parameter list yet. All |
|
300 // variables are reset in that case |
|
301 data.parametersFound = false; |
|
302 data.functionEndIndex = -1; |
|
303 data.functionStartIndex = -1; |
|
304 data.classEndIndex = -1; |
|
305 processParametersNotFoundCharacter(data, c); |
|
306 } else if (data.functionEndIndex == -1) { |
|
307 processFunctionNameNotFoundCharacter(data, c); |
|
308 } else if (data.functionStartIndex == -1) { |
|
309 processFunctionNameCharacter(context, data, c); |
|
310 } else if (data.classEndIndex == -1) { |
|
311 processClassNameNotFoundCharacter(data); |
|
312 } else if (data.classStartIndex == -1) { |
|
313 processClassNameCharacter(context, data, c); |
|
314 } else { |
|
315 processReturnTypeCharacter(context, data); |
|
316 } |
|
317 } |
|
318 if (!data.finished) { |
|
319 processContextTerminator(context, data, true); |
|
320 } |
|
321 return data.valid; |
|
322 } |
|
323 |
|
324 /** |
|
325 * Processes a character after class and function names have been found |
|
326 * |
|
327 * @param context |
|
328 * the context |
|
329 * @param data |
|
330 * the search data |
|
331 * @throws SourceParserException |
|
332 * if processing fails |
|
333 */ |
|
334 private void processReturnTypeCharacter(SourceContext context, |
|
335 ContextSearchData data) throws SourceParserException { |
|
336 if (data.itr.hasSkipped()) { |
|
337 // Collects all return type candidates to the context |
|
338 addReturnType(context, data.itr.previousIndex(), |
|
339 data.returnEndIndex); |
|
340 data.returnEndIndex = data.itr.currentIndex(); |
|
341 } |
|
342 } |
|
343 |
|
344 /** |
|
345 * Processes a character after function name has been found, but class name |
|
346 * has not yet been found |
|
347 * |
|
348 * @param data |
|
349 * the search flags |
|
350 */ |
|
351 private void processClassNameNotFoundCharacter(ContextSearchData data) { |
|
352 // After start of function and the separator has been found, the |
|
353 // next character marks the end of class name |
|
354 data.classEndIndex = data.itr.currentIndex() + 1; |
|
355 } |
|
356 |
|
357 /** |
|
358 * Parses a character which belongs to the class name |
|
359 * |
|
360 * @param context |
|
361 * the source context to be parsed |
|
362 * @param data |
|
363 * the context search parameters |
|
364 * @param c |
|
365 * the character |
|
366 * @throws SourceParserException |
|
367 * if processing fails |
|
368 */ |
|
369 private void processClassNameCharacter(SourceContext context, |
|
370 ContextSearchData data, char c) throws SourceParserException { |
|
371 if (data.itr.hasSkipped() || (c == ':' && data.itr.peek() == ':')) { |
|
372 // Start of class name is found when iterator skips over |
|
373 // white space or comment characters or in case of nested class |
|
374 // separator character has been found |
|
375 context.setFunctionName(parser.getSource().get( |
|
376 data.functionStartIndex, |
|
377 data.functionEndIndex - data.functionStartIndex)); |
|
378 data.classStartIndex = data.itr.previousIndex(); |
|
379 data.returnEndIndex = data.itr.currentIndex(); |
|
380 context.setClassName(parser.getSource().get(data.classStartIndex, |
|
381 data.classEndIndex - data.classStartIndex)); |
|
382 |
|
383 // In case of nested class skips over the second ':' |
|
384 if (c == ':' && data.itr.peek() == ':') { |
|
385 data.itr.next(); |
|
386 } |
|
387 } |
|
388 } |
|
389 |
|
390 /** |
|
391 * Processes a character while within function name |
|
392 * |
|
393 * @param context |
|
394 * the source context under processing |
|
395 * @param data |
|
396 * the context search flags |
|
397 * @param c |
|
398 * the character |
|
399 * @throws SourceParserException |
|
400 * if processing fails |
|
401 */ |
|
402 private void processFunctionNameCharacter(SourceContext context, |
|
403 ContextSearchData data, char c) throws SourceParserException { |
|
404 // After end of function has been found the separator character |
|
405 // marks the start of function |
|
406 if (c == ':') { |
|
407 if (data.itr.hasNext() && data.itr.peek() == ':') { |
|
408 data.functionStartIndex = data.itr.previousIndex(); |
|
409 context.setFunctionName(parser.getSource().get( |
|
410 data.functionStartIndex, |
|
411 data.functionEndIndex - data.functionStartIndex)); |
|
412 // Skips over the second ':' |
|
413 data.itr.next(); |
|
414 } else { |
|
415 // Only one ':' character -> Invalid |
|
416 data.finished = true; |
|
417 } |
|
418 } else if (data.itr.hasSkipped()) { |
|
419 // If the iterator skipped over some characters and the next |
|
420 // character is not ':' the function is a non-member |
|
421 data.functionStartIndex = data.itr.previousIndex(); |
|
422 context.setFunctionName(parser.getSource().get( |
|
423 data.functionStartIndex, |
|
424 data.functionEndIndex - data.functionStartIndex)); |
|
425 // Class name indices are set so parser does not search for them |
|
426 data.classStartIndex = data.itr.previousIndex(); |
|
427 data.classEndIndex = data.itr.previousIndex(); |
|
428 data.returnEndIndex = data.itr.currentIndex(); |
|
429 } |
|
430 } |
|
431 |
|
432 /** |
|
433 * Processes a character when function name has not yet been found |
|
434 * |
|
435 * @param data |
|
436 * the search flags |
|
437 * @param c |
|
438 * the character to be processed |
|
439 */ |
|
440 private void processFunctionNameNotFoundCharacter(ContextSearchData data, |
|
441 char c) { |
|
442 // The next character after parameters is the end of function |
|
443 if (c == ':') { |
|
444 data.finished = true; |
|
445 } |
|
446 data.functionEndIndex = data.itr.currentIndex() + 1; |
|
447 } |
|
448 |
|
449 /** |
|
450 * Checks if the character is '(' or ')' and updates the parametersFound |
|
451 * flag accordingly |
|
452 * |
|
453 * @param data |
|
454 * the search data |
|
455 * @param c |
|
456 * the current character |
|
457 */ |
|
458 private void processParametersNotFoundCharacter(ContextSearchData data, |
|
459 char c) { |
|
460 if (c == ')') { |
|
461 data.inParameters++; |
|
462 } else if (c == '(') { |
|
463 data.inParameters--; |
|
464 if (data.inParameters == 0) { |
|
465 data.context.setParametersStartIndex(data.itr.currentIndex()); |
|
466 data.parametersFound = true; |
|
467 } |
|
468 } |
|
469 } |
|
470 |
|
471 /** |
|
472 * Processes a context terminating character |
|
473 * |
|
474 * @param context |
|
475 * the context under processing |
|
476 * @param data |
|
477 * the search data |
|
478 * @param startOfFile |
|
479 * context was terminated due to start of file |
|
480 * @throws SourceParserException |
|
481 * if processing fails |
|
482 */ |
|
483 private void processContextTerminator(SourceContext context, |
|
484 ContextSearchData data, boolean startOfFile) |
|
485 throws SourceParserException { |
|
486 int offset = startOfFile ? data.itr.currentIndex() : data.itr |
|
487 .previousIndex(); |
|
488 if (data.classStartIndex != -1) { |
|
489 addReturnType(context, offset, data.returnEndIndex); |
|
490 data.valid = true; |
|
491 } else if (data.classEndIndex != -1) { |
|
492 context.setClassName(parser.getSource().get(offset, |
|
493 data.classEndIndex - offset)); |
|
494 data.valid = true; |
|
495 } else if (data.functionEndIndex != -1) { |
|
496 context.setFunctionName(parser.getSource().get(offset, |
|
497 data.functionEndIndex - offset)); |
|
498 data.valid = true; |
|
499 } |
|
500 // Finished flag is set. If function name was not found, the valid flag |
|
501 // remains false |
|
502 data.finished = true; |
|
503 } |
|
504 |
|
505 /** |
|
506 * Adds a return type to the context |
|
507 * |
|
508 * @param context |
|
509 * the context to be searched |
|
510 * @param start |
|
511 * the start index |
|
512 * @param end |
|
513 * the end index |
|
514 * @throws SourceParserException |
|
515 * if return type cannot be added |
|
516 */ |
|
517 private void addReturnType(SourceContext context, int start, int end) |
|
518 throws SourceParserException { |
|
519 context.addReturnType(parser.getSource().get(start, end - start + 1)); |
|
520 } |
|
521 } |