|
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 comments and strings |
|
17 * |
|
18 */ |
|
19 package com.nokia.tracecompiler.source; |
|
20 |
|
21 import java.util.ArrayList; |
|
22 import java.util.Collections; |
|
23 import java.util.List; |
|
24 |
|
25 /** |
|
26 * Parser for comments and strings |
|
27 * |
|
28 */ |
|
29 final class ExcludedAreaParser { |
|
30 |
|
31 /** |
|
32 * The check range is used to limit the effect of unterminated ' in code |
|
33 */ |
|
34 private static final int CHAR_CHECK_RANGE = 3; // CodForChk_Dis_Magic |
|
35 |
|
36 /** |
|
37 * Source parser |
|
38 */ |
|
39 private SourceParser parser; |
|
40 |
|
41 /** |
|
42 * List of source file areas that are not used in search |
|
43 */ |
|
44 private ArrayList<SourceExcludedArea> excludedAreas = new ArrayList<SourceExcludedArea>(); |
|
45 |
|
46 /** |
|
47 * Comparator for array sorting and searching |
|
48 */ |
|
49 private PositionArrayComparator arrayComparator = new PositionArrayComparator(); |
|
50 |
|
51 /** |
|
52 * Constructor |
|
53 * |
|
54 * @param parser |
|
55 * the source parser |
|
56 */ |
|
57 ExcludedAreaParser(SourceParser parser) { |
|
58 this.parser = parser; |
|
59 } |
|
60 |
|
61 /** |
|
62 * Resets the excluded areas |
|
63 */ |
|
64 void reset() { |
|
65 excludedAreas.clear(); |
|
66 } |
|
67 |
|
68 /** |
|
69 * Finds the array index of the excluded area which contains the offset. If |
|
70 * none of the areas contain the offset, returns negative integer indicating |
|
71 * the index of the excluded area following the offset |
|
72 * |
|
73 * @param offset |
|
74 * the offset to the data |
|
75 * @return the excluded area index |
|
76 */ |
|
77 int find(int offset) { |
|
78 return Collections.binarySearch(excludedAreas, new SourceLocationBase( |
|
79 parser, offset), arrayComparator); |
|
80 } |
|
81 |
|
82 /** |
|
83 * Finds the excluded source file areas. Excluded areas include comments and |
|
84 * quoted strings. Overwrites possible old areas. |
|
85 * |
|
86 * @throws SourceParserException |
|
87 * if processing fails |
|
88 */ |
|
89 void parseAll() throws SourceParserException { |
|
90 excludedAreas.clear(); |
|
91 ExcludedAreaSearchData data = new ExcludedAreaSearchData(); |
|
92 int length = parser.getSource().getLength(); |
|
93 SourceExcludedArea lastarea = parse(data, length); |
|
94 if (data.inString || data.inChar || data.inComment |
|
95 || data.inLineComment || data.inPreprocessor) { |
|
96 lastarea.setLength(parser.getSource().getLength() |
|
97 - lastarea.getOffset()); |
|
98 excludedAreas.add(lastarea); |
|
99 } |
|
100 } |
|
101 |
|
102 /** |
|
103 * Parses the excluded areas of source |
|
104 * |
|
105 * @param data |
|
106 * the search data |
|
107 * @param length |
|
108 * the length of data to be parsed |
|
109 * @return the last area |
|
110 * @throws SourceParserException |
|
111 * if parser fails |
|
112 */ |
|
113 private SourceExcludedArea parse(ExcludedAreaSearchData data, int length) |
|
114 throws SourceParserException { |
|
115 SourceExcludedArea area = null; |
|
116 while (data.index < length) { |
|
117 data.value = parser.getSource().getChar(data.index++); |
|
118 // Line comments end at end-of-line |
|
119 if (data.inLineComment) { |
|
120 processInLineComment(data, area); |
|
121 } else if (data.inComment) { |
|
122 processInComment(data, area); |
|
123 } else if (data.inPreprocessor) { |
|
124 processInPreprocessor(data, area); |
|
125 } else if (data.inString) { |
|
126 processInString(data, area); |
|
127 } else if (data.inChar) { |
|
128 processInChar(data, area); |
|
129 } else if (data.value == '/' && data.index < length) { |
|
130 area = createCommentArea(data); |
|
131 } else if (data.value == '\"') { |
|
132 area = createStringArea(data); |
|
133 } else if (data.value == '\'') { |
|
134 area = createCharArea(data); |
|
135 } else if (data.value == '#' |
|
136 && (data.index == 1 || parser.getSource().getChar( |
|
137 data.index - 2) == '\n')) { // CodForChk_Dis_Magic |
|
138 area = createPreprocessorArea(data); |
|
139 } |
|
140 } |
|
141 return area; |
|
142 } |
|
143 |
|
144 /** |
|
145 * Gets the excluded area that contains given offset |
|
146 * |
|
147 * @param offset |
|
148 * the offset to the area |
|
149 * @return the area or null if offset does not hit any area |
|
150 */ |
|
151 SourceExcludedArea getArea(int offset) { |
|
152 SourceExcludedArea retval; |
|
153 int index = find(offset); |
|
154 if (index >= 0) { |
|
155 retval = excludedAreas.get(index); |
|
156 } else { |
|
157 retval = null; |
|
158 } |
|
159 return retval; |
|
160 } |
|
161 |
|
162 /** |
|
163 * Gets the list of excluded areas |
|
164 * |
|
165 * @return the list of areas |
|
166 */ |
|
167 List<SourceExcludedArea> getAreas() { |
|
168 return excludedAreas; |
|
169 } |
|
170 |
|
171 /** |
|
172 * Processes a quote (') character marking start of character area |
|
173 * |
|
174 * @param data |
|
175 * the search flags |
|
176 * @return the new area |
|
177 */ |
|
178 private SourceExcludedArea createCharArea(ExcludedAreaSearchData data) { |
|
179 SourceExcludedArea area; |
|
180 data.inChar = true; |
|
181 area = new SourceExcludedArea(parser, data.index - 1, |
|
182 SourceExcludedArea.CHARACTER); |
|
183 return area; |
|
184 } |
|
185 |
|
186 /** |
|
187 * Processes a double quote (") character marking start of string area |
|
188 * |
|
189 * @param data |
|
190 * the search flags |
|
191 * @return the new area |
|
192 */ |
|
193 private SourceExcludedArea createStringArea(ExcludedAreaSearchData data) { |
|
194 SourceExcludedArea area; |
|
195 data.inString = true; |
|
196 area = new SourceExcludedArea(parser, data.index - 1, |
|
197 SourceExcludedArea.STRING); |
|
198 return area; |
|
199 } |
|
200 |
|
201 /** |
|
202 * Processes a forward slash (/) character marking start of comment |
|
203 * |
|
204 * @param data |
|
205 * the search flags |
|
206 * @return the comment object |
|
207 * @throws SourceParserException |
|
208 * if processing fails |
|
209 */ |
|
210 private SourceExcludedArea createCommentArea(ExcludedAreaSearchData data) |
|
211 throws SourceParserException { |
|
212 SourceExcludedArea area; |
|
213 char next = parser.getSource().getChar(data.index); |
|
214 if (next == '/') { |
|
215 data.inLineComment = true; |
|
216 area = new SourceExcludedArea(parser, data.index - 1, |
|
217 SourceExcludedArea.LINE_COMMENT); |
|
218 data.index++; |
|
219 } else if (next == '*') { |
|
220 data.inComment = true; |
|
221 area = new SourceExcludedArea(parser, data.index - 1, |
|
222 SourceExcludedArea.MULTILINE_COMMENT); |
|
223 data.index++; |
|
224 } else { |
|
225 area = null; |
|
226 } |
|
227 return area; |
|
228 } |
|
229 |
|
230 /** |
|
231 * Processes a preprocessor definition |
|
232 * |
|
233 * @param data |
|
234 * the search flags |
|
235 * @return the preprocessor area representation |
|
236 */ |
|
237 private SourceExcludedArea createPreprocessorArea( |
|
238 ExcludedAreaSearchData data) { |
|
239 SourceExcludedArea area = new SourceExcludedArea(parser, |
|
240 data.index - 1, SourceExcludedArea.PREPROCESSOR_DEFINITION); |
|
241 data.inPreprocessor = true; |
|
242 return area; |
|
243 } |
|
244 |
|
245 /** |
|
246 * Processes a character that belongs to '' area |
|
247 * |
|
248 * @param data |
|
249 * the search flags |
|
250 * @param area |
|
251 * the area under processing |
|
252 * @throws SourceParserException |
|
253 * if processing fails |
|
254 */ |
|
255 private void processInChar(ExcludedAreaSearchData data, |
|
256 SourceExcludedArea area) throws SourceParserException { |
|
257 // The check range is used to limit the effect of unterminated ' |
|
258 if ((data.value == '\'' && parser.getSource().getChar(data.index - 2) != '\\') // CodForChk_Dis_Magic |
|
259 || data.index - area.getOffset() > CHAR_CHECK_RANGE) { |
|
260 data.inChar = false; |
|
261 area.setLength(data.index - area.getOffset()); |
|
262 excludedAreas.add(area); |
|
263 } |
|
264 } |
|
265 |
|
266 /** |
|
267 * Processes a character that belongs to "" area |
|
268 * |
|
269 * @param data |
|
270 * the search flags |
|
271 * @param area |
|
272 * the area under processing |
|
273 * @throws SourceParserException |
|
274 * if processing fails |
|
275 */ |
|
276 private void processInString(ExcludedAreaSearchData data, |
|
277 SourceExcludedArea area) throws SourceParserException { |
|
278 // Strings end with " unless escaped with \" (except \\") |
|
279 if (data.value == '\"') { |
|
280 if (parser.getSource().getChar(data.index - 2) != '\\' // CodForChk_Dis_Magic |
|
281 || parser.getSource().getChar(data.index - 3) == '\\') { // CodForChk_Dis_Magic |
|
282 data.inString = false; |
|
283 area.setLength(data.index - area.getOffset()); |
|
284 excludedAreas.add(area); |
|
285 } |
|
286 } |
|
287 } |
|
288 |
|
289 /** |
|
290 * Processes a character that belongs to multi-line comment |
|
291 * |
|
292 * @param data |
|
293 * the search flags |
|
294 * @param area |
|
295 * the area under processing |
|
296 * @throws SourceParserException |
|
297 * if processing fails |
|
298 */ |
|
299 private void processInComment(ExcludedAreaSearchData data, |
|
300 SourceExcludedArea area) throws SourceParserException { |
|
301 // Comments end with */ |
|
302 if (data.value == '*') { |
|
303 if (data.index < parser.getSource().getLength() |
|
304 && parser.getSource().getChar(data.index) == '/') { |
|
305 data.index++; |
|
306 data.inComment = false; |
|
307 area.setLength(data.index - area.getOffset()); |
|
308 excludedAreas.add(area); |
|
309 } |
|
310 } |
|
311 } |
|
312 |
|
313 /** |
|
314 * Processes a character that belongs to line comment |
|
315 * |
|
316 * @param data |
|
317 * the search flags |
|
318 * @param area |
|
319 * the area under processing |
|
320 */ |
|
321 private void processInLineComment(ExcludedAreaSearchData data, |
|
322 SourceExcludedArea area) { |
|
323 if (data.value == '\n') { |
|
324 data.inLineComment = false; |
|
325 area.setLength(data.index - area.getOffset()); |
|
326 excludedAreas.add(area); |
|
327 } |
|
328 } |
|
329 |
|
330 /** |
|
331 * Processes a character that belongs to preprocessor definition |
|
332 * |
|
333 * @param data |
|
334 * the search flags |
|
335 * @param area |
|
336 * the area under processing |
|
337 * @throws SourceParserException |
|
338 * if processing fails |
|
339 */ |
|
340 private void processInPreprocessor(ExcludedAreaSearchData data, |
|
341 SourceExcludedArea area) throws SourceParserException { |
|
342 if (data.value == '\n') { |
|
343 char prev = parser.getSource().getChar(data.index - 2); // CodForChk_Dis_Magic |
|
344 char prev2 = parser.getSource().getChar(data.index - 3); // CodForChk_Dis_Magic |
|
345 if (!((prev == '\\') || (prev == '\r' && prev2 == '\\'))) { |
|
346 data.inPreprocessor = false; |
|
347 area.setLength(data.index - area.getOffset()); |
|
348 excludedAreas.add(area); |
|
349 } |
|
350 } |
|
351 } |
|
352 } |