1 /* |
|
2 * Copyright (c) 2010 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 * Properties of a source document opened to Eclipse editor |
|
17 * |
|
18 */ |
|
19 package com.nokia.tracecompiler.engine.source; |
|
20 |
|
21 import java.util.ArrayList; |
|
22 import java.util.Iterator; |
|
23 |
|
24 import com.nokia.tracecompiler.engine.TraceCompilerEngineConfiguration; |
|
25 import com.nokia.tracecompiler.engine.TraceCompilerEngineGlobals; |
|
26 import com.nokia.tracecompiler.engine.TraceLocation; |
|
27 import com.nokia.tracecompiler.model.TraceModel; |
|
28 import com.nokia.tracecompiler.source.SourceDocumentFactory; |
|
29 import com.nokia.tracecompiler.source.SourceDocumentInterface; |
|
30 import com.nokia.tracecompiler.source.SourceIterator; |
|
31 import com.nokia.tracecompiler.source.SourceParser; |
|
32 import com.nokia.tracecompiler.source.SourceParserException; |
|
33 import com.nokia.tracecompiler.source.SourcePropertyProvider; |
|
34 import com.nokia.tracecompiler.source.SourceStringSearch; |
|
35 |
|
36 /** |
|
37 * Properties of a source document which contains trace locations |
|
38 * |
|
39 */ |
|
40 public class SourceProperties implements Iterable<TraceLocation> { |
|
41 |
|
42 /** |
|
43 * Trace locations within the source |
|
44 */ |
|
45 private ArrayList<TraceLocation> locations = new ArrayList<TraceLocation>(); |
|
46 |
|
47 /** |
|
48 * Source parser |
|
49 */ |
|
50 private SourceParser sourceParser; |
|
51 |
|
52 /** |
|
53 * Offset is stored in preProcess and reset in postProcess. |
|
54 */ |
|
55 private int firstChangedLocation = -1; |
|
56 |
|
57 /** |
|
58 * Offset is stored in preProcess and reset in postProcess. |
|
59 */ |
|
60 private int firstUnchangedLocation = -1; |
|
61 |
|
62 /** |
|
63 * The searchers for trace identifiers |
|
64 */ |
|
65 private ArrayList<SourceStringSearch> searchers = new ArrayList<SourceStringSearch>(); |
|
66 |
|
67 /** |
|
68 * Start index for calls to parseTrace |
|
69 */ |
|
70 private int searchStartIndex; |
|
71 |
|
72 /** |
|
73 * Read-only flag |
|
74 */ |
|
75 private boolean readOnly; |
|
76 |
|
77 /** |
|
78 * Creates source properties for given source document |
|
79 * |
|
80 * @param model |
|
81 * the trace model |
|
82 * @param framework |
|
83 * the document framework |
|
84 * @param document |
|
85 * the document |
|
86 */ |
|
87 SourceProperties(TraceModel model, SourceDocumentFactory framework, |
|
88 SourceDocumentInterface document) { |
|
89 sourceParser = new SourceParser(framework, document); |
|
90 Iterator<SourceParserRule> parsers = model |
|
91 .getExtensions(SourceParserRule.class); |
|
92 while (parsers.hasNext()) { |
|
93 // The rule defines what to search and how to interpret the |
|
94 // parameters. It is stored into the searcher as search data |
|
95 addParserRule(parsers.next()); |
|
96 } |
|
97 } |
|
98 |
|
99 /** |
|
100 * Gets the source parser |
|
101 * |
|
102 * @return the parser |
|
103 */ |
|
104 public SourceParser getSourceParser() { |
|
105 return sourceParser; |
|
106 } |
|
107 |
|
108 /* |
|
109 * (non-Javadoc) |
|
110 * |
|
111 * @see java.lang.Iterable#iterator() |
|
112 */ |
|
113 public Iterator<TraceLocation> iterator() { |
|
114 return locations.iterator(); |
|
115 } |
|
116 |
|
117 /** |
|
118 * Gets the file name of this source |
|
119 * |
|
120 * @return the name |
|
121 */ |
|
122 public String getFileName() { |
|
123 String retval = null; |
|
124 if (sourceParser != null) { |
|
125 SourceDocumentInterface source = sourceParser.getSource(); |
|
126 if (source != null) { |
|
127 SourcePropertyProvider provider = source.getPropertyProvider(); |
|
128 if (provider != null) { |
|
129 retval = provider.getFileName(); |
|
130 } |
|
131 } |
|
132 } |
|
133 return retval; |
|
134 } |
|
135 |
|
136 /** |
|
137 * Sets the read-only flag for this source. Traces cannot be added to |
|
138 * read-only sources, but they can be parsed for data |
|
139 * |
|
140 * @param readOnly |
|
141 * the read-only flag |
|
142 */ |
|
143 void setReadOnly(boolean readOnly) { |
|
144 this.readOnly = readOnly; |
|
145 } |
|
146 |
|
147 /** |
|
148 * Gets the read-only flag |
|
149 * |
|
150 * @return read-only flag |
|
151 */ |
|
152 public boolean isReadOnly() { |
|
153 return readOnly; |
|
154 } |
|
155 |
|
156 /** |
|
157 * Source opened notification |
|
158 */ |
|
159 void sourceOpened() { |
|
160 updateTraces(0, sourceParser.getDataLength()); |
|
161 } |
|
162 |
|
163 /** |
|
164 * Parses the document starting from given offset and locates the trace |
|
165 * entries from it. The first unchanged trace entry stops the search |
|
166 * |
|
167 * @param startOffset |
|
168 * the offset where to start the search |
|
169 * @param endOffset |
|
170 * the offset where to end the search |
|
171 */ |
|
172 private void updateTraces(int startOffset, int endOffset) { |
|
173 Iterator<SourceStringSearch> itr = searchers.iterator(); |
|
174 while (itr.hasNext()) { |
|
175 SourceStringSearch searcher = itr.next(); |
|
176 searcher.resetSearch(startOffset, endOffset); |
|
177 updateTraces(endOffset, searcher); |
|
178 } |
|
179 } |
|
180 |
|
181 /** |
|
182 * Uses the given SourceSearch to parse traces |
|
183 * |
|
184 * @param end |
|
185 * the offset where parser should stop |
|
186 * @param searcher |
|
187 * the searcher |
|
188 */ |
|
189 private void updateTraces(int end, SourceStringSearch searcher) { |
|
190 int offset; |
|
191 searchStartIndex = 0; |
|
192 // If not updating, the entries contents are processed |
|
193 do { |
|
194 offset = searcher.findNext(); |
|
195 try { |
|
196 if (offset != -1 && offset < end) { |
|
197 String tag = isValidTrace(offset, searcher |
|
198 .getSearchString().length(), searcher, false); |
|
199 if (tag != null) { |
|
200 parseTrace(offset, (SourceParserRule) searcher |
|
201 .getSearchData(), tag); |
|
202 } |
|
203 } |
|
204 } catch (SourceParserException e) { |
|
205 TraceLocation location = new TraceLocation(this, offset, |
|
206 offset + 80); |
|
207 TraceCompilerEngineGlobals |
|
208 .getEvents() |
|
209 .postErrorMessage( |
|
210 Messages |
|
211 .getString("SourceProperties.parsingArrowAtBeginText") + location.getFilePath() + location.getFileName() + Messages.getString("SourceProperties.parsingArrownAtMiddleText") + location.getLineNumber(), null, true); //$NON-NLS-1$ //$NON-NLS-2$ |
|
212 // If the parameters cannot be parsed, the trace is |
|
213 // not added to the array |
|
214 } |
|
215 } while (offset != -1 && offset < end); |
|
216 } |
|
217 |
|
218 /** |
|
219 * Parses a trace found from the document and adds it to the document's list |
|
220 * of positions. The position updater keeps the trace location up-to-date. |
|
221 * |
|
222 * @param offset |
|
223 * the offset to the trace |
|
224 * @param parserRule |
|
225 * the parser to be attached to the location |
|
226 * @param locationTag |
|
227 * the tag of the location |
|
228 * @throws SourceParserException |
|
229 * if trace cannot be parsed |
|
230 */ |
|
231 private void parseTrace(int offset, SourceParserRule parserRule, |
|
232 String locationTag) throws SourceParserException { |
|
233 int arrayIndex = -1; |
|
234 // Checks the changed locations. If a matching offset if found, the |
|
235 // location is an existing one. In that case the location is not |
|
236 // added to the array. If an offset larger than the new offset is |
|
237 // found from the array, the location is inserted into that slot. If |
|
238 // all locations within the array are smaller than the new offset, |
|
239 // the new location is inserted before the first unchanged location. |
|
240 // Since the locations in the array are ordered, the checking can |
|
241 // always start from the latest location that has been found from |
|
242 // the array. The caller of this function must set |
|
243 // parseTraceStartIndex to 0 before starting a loop where this |
|
244 // function is called. If firstUnchangedLocation is -1, this is the |
|
245 // first time the file is being parsed and thus all locations are |
|
246 // checked |
|
247 boolean found = false; |
|
248 int searchEndIndex; |
|
249 int newSearchStartIndex = -1; |
|
250 if (firstUnchangedLocation >= 0) { |
|
251 searchEndIndex = firstUnchangedLocation; |
|
252 } else { |
|
253 searchEndIndex = locations.size(); |
|
254 } |
|
255 for (int i = searchStartIndex; i < searchEndIndex && !found; i++) { |
|
256 TraceLocation location = locations.get(i); |
|
257 // Deleted locations are ignored. If a trace was replaced, the |
|
258 // new offset will match the offset of the deleted one. |
|
259 if (!location.isDeleted()) { |
|
260 // If the offset of the trace matches an existing offset, |
|
261 // the trace is old one. If the offset within the array is |
|
262 // larger than the source offset, the trace found from |
|
263 // source is new. |
|
264 if (location.getOffset() == offset) { |
|
265 found = true; |
|
266 // Starts the next search from the value following the |
|
267 // trace that was found |
|
268 searchStartIndex = i + 1; |
|
269 arrayIndex = -1; |
|
270 } else if (location.getOffset() > offset) { |
|
271 found = true; |
|
272 // A new trace will be added into the current index, so |
|
273 // the next search will start from the same location as |
|
274 // was checked now. The index is updated after the trace has |
|
275 // succesfully been created |
|
276 newSearchStartIndex = i + 1; |
|
277 arrayIndex = i; |
|
278 } |
|
279 } |
|
280 } |
|
281 // If trace was not found from the list, the trace is new and all |
|
282 // traces following it are also new. The start index is set to point |
|
283 // past the first unchanged location and thus the next search will |
|
284 // ignore the above loop. |
|
285 if (!found) { |
|
286 arrayIndex = searchEndIndex; |
|
287 searchStartIndex = firstUnchangedLocation + 1; |
|
288 } |
|
289 if (arrayIndex >= 0) { |
|
290 // Creates a new location if it was not found |
|
291 ArrayList<String> list = new ArrayList<String>(); |
|
292 int endOfTrace = sourceParser |
|
293 .tokenizeParameters(offset, list, true); |
|
294 |
|
295 TraceLocation location = new TraceLocation(this, offset, endOfTrace |
|
296 - offset); |
|
297 |
|
298 // The parser rules have been associated with the searchers. The |
|
299 // parser rule that found the location is associated with the |
|
300 // location and used to process its parameters |
|
301 location.setTag(locationTag); |
|
302 location.setParserRule(parserRule); |
|
303 location.setData(list); |
|
304 |
|
305 TraceCompilerEngineGlobals |
|
306 .getEvents() |
|
307 .postInfoMessage( |
|
308 Messages |
|
309 .getString("SourceProperties.newTraceLocationFoundBeginText") + location.getFilePath() + location.getFileName() + Messages.getString("SourceProperties.newTraceLocationFoundMiddleText") + location.getLineNumber() + Messages.getString("SourceProperties.newTraceLocationFoundEndText") + location.getTraceText(), null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
|
310 |
|
311 locations.add(arrayIndex, location); |
|
312 // The changed flag is set to newly added traces. If a location |
|
313 // is added prior to the first changed location, the index of first |
|
314 // changed location needs to be adjusted so that the flag gets |
|
315 // cleared in postprocessing. Also the index of first unchanged |
|
316 // location needs to be updated to reflect the changed array |
|
317 if (firstUnchangedLocation >= 0) { |
|
318 location.setContentChanged(true); |
|
319 if (arrayIndex < firstChangedLocation) { |
|
320 firstChangedLocation = arrayIndex; |
|
321 } |
|
322 firstUnchangedLocation++; |
|
323 } |
|
324 // Updates the search start index if trace creation was succesful |
|
325 if (newSearchStartIndex >= 0) { |
|
326 searchStartIndex = newSearchStartIndex; |
|
327 } |
|
328 } |
|
329 } |
|
330 |
|
331 /** |
|
332 * Checks that a trace is valid |
|
333 * |
|
334 * @param offset |
|
335 * offset to trace identifier |
|
336 * @param length |
|
337 * length of trace |
|
338 * @param searcher |
|
339 * the source searcher |
|
340 * @param checkMainTag |
|
341 * true if the main search tag needs to be checked, false if only |
|
342 * the tag suffix is checked |
|
343 * @return the trace tag or null if trace is not valid |
|
344 */ |
|
345 private String isValidTrace(int offset, int length, |
|
346 SourceStringSearch searcher, boolean checkMainTag) { |
|
347 String retval = null; |
|
348 try { |
|
349 int idlen = searcher.getSearchString().length(); |
|
350 int idend = offset + idlen; |
|
351 if (checkMainTag) { |
|
352 if (length >= idlen |
|
353 && searcher.isSearchStringMatch(sourceParser.getData( |
|
354 offset, idlen))) { |
|
355 // The previous character must be a separator or white space |
|
356 if (offset == 0 |
|
357 || !Character.isJavaIdentifierPart(sourceParser |
|
358 .getData(offset - 1))) { |
|
359 retval = getSearchTag(offset, idend); |
|
360 } |
|
361 } |
|
362 } else { |
|
363 // If main tag is not checked |
|
364 retval = getSearchTag(offset, idend); |
|
365 } |
|
366 retval = verifyTag(searcher, retval, idlen); |
|
367 } catch (Exception e) { |
|
368 if (TraceCompilerEngineConfiguration.ASSERTIONS_ENABLED) { |
|
369 TraceCompilerEngineGlobals.getEvents().postAssertionFailed( |
|
370 "Trace validity check failed", e); //$NON-NLS-1$ |
|
371 } |
|
372 } |
|
373 return retval; |
|
374 } |
|
375 |
|
376 /** |
|
377 * Verifies the tag against tag suffixes from parser |
|
378 * |
|
379 * @param searcher |
|
380 * the searcher |
|
381 * @param tag |
|
382 * the tag include main tag and suffix |
|
383 * @param idlen |
|
384 * the length of the main tag |
|
385 * @return the tag if it is valid, null if not |
|
386 */ |
|
387 private String verifyTag(SourceStringSearch searcher, String tag, int idlen) { |
|
388 if (tag != null) { |
|
389 // The trace suffix is verified by the parser. For example, if |
|
390 // search data is "SymbianTrace" and the tag found from source |
|
391 // is "SymbianTraceData1", the parser checks if "Data1" is a |
|
392 // valid trace tag suffix. |
|
393 if (!((SourceParserRule) searcher.getSearchData()) |
|
394 .isAllowedTagSuffix(tag.substring(idlen))) { |
|
395 tag = null; |
|
396 } |
|
397 } |
|
398 return tag; |
|
399 } |
|
400 |
|
401 /** |
|
402 * Gets the search tag between offset and next '(' character |
|
403 * |
|
404 * @param offset |
|
405 * the start of tag |
|
406 * @param idend |
|
407 * the end of tag |
|
408 * @return the tag |
|
409 * @throws SourceParserException |
|
410 * if parser fails |
|
411 */ |
|
412 private String getSearchTag(int offset, int idend) |
|
413 throws SourceParserException { |
|
414 // Locates the parameters starting from trace identifier |
|
415 String retval = null; |
|
416 SourceIterator srcitr = sourceParser.createIterator(idend - 1, |
|
417 SourceParser.SKIP_ALL); |
|
418 boolean found = false; |
|
419 while (srcitr.hasNext() && !found) { |
|
420 char c = srcitr.next(); |
|
421 if (c == ';') { |
|
422 // Trace must have parameters |
|
423 found = true; |
|
424 } else if (c == '(') { |
|
425 found = true; |
|
426 // Stores the tag into location |
|
427 retval = sourceParser.getData(offset, srcitr.previousIndex() |
|
428 - offset + 1); |
|
429 } else if (srcitr.hasSkipped()) { |
|
430 // White spaces are not allowed within trace tag |
|
431 found = true; |
|
432 } |
|
433 } |
|
434 return retval; |
|
435 } |
|
436 |
|
437 /** |
|
438 * Checks if a trace can be inserted into given location |
|
439 * |
|
440 * @param offset |
|
441 * the offset to the location |
|
442 * @return true if location is valid |
|
443 */ |
|
444 boolean checkInsertLocation(int offset) { |
|
445 boolean retval = true; |
|
446 try { |
|
447 offset = sourceParser.findStartOfLine(offset, false, true); |
|
448 if (sourceParser.isInExcludedArea(offset)) { |
|
449 retval = false; |
|
450 } |
|
451 } catch (SourceParserException e) { |
|
452 retval = false; |
|
453 } |
|
454 return retval; |
|
455 } |
|
456 |
|
457 /** |
|
458 * Adds a new parser |
|
459 * |
|
460 * @param rule |
|
461 * the new parser rule |
|
462 */ |
|
463 void addParserRule(SourceParserRule rule) { |
|
464 SourceStringSearch searcher = sourceParser.startStringSearch(rule |
|
465 .getSearchTag(), 0, -1, SourceParser.MATCH_WORD_BEGINNING |
|
466 | SourceParser.SKIP_ALL); |
|
467 searcher.setSearchData(rule); |
|
468 searchers.add(searcher); |
|
469 } |
|
470 |
|
471 } |
|