tmf: annotate TmfContext#location as nullable
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.core / src / org / eclipse / tracecompass / tmf / core / trace / text / TextTrace.java
CommitLineData
bcb8c2cb 1/*******************************************************************************
6c230e50 2 * Copyright (c) 2012, 2015 Ericsson
bcb8c2cb
PT
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Patrick Tasse - Initial API and implementation
738a2796 11 * Marc-Andre Laperle - Add persistent index support
bcb8c2cb
PT
12 *******************************************************************************/
13
2bdf0193 14package org.eclipse.tracecompass.tmf.core.trace.text;
bcb8c2cb
PT
15
16import java.io.File;
17import java.io.IOException;
738a2796 18import java.nio.ByteBuffer;
543e2b1e
BH
19import java.util.Collections;
20import java.util.List;
bcb8c2cb
PT
21import java.util.regex.Matcher;
22import java.util.regex.Pattern;
23
24import org.eclipse.core.resources.IProject;
25import org.eclipse.core.resources.IResource;
26import org.eclipse.core.runtime.IStatus;
27import org.eclipse.core.runtime.Status;
6c230e50 28import org.eclipse.jdt.annotation.NonNull;
4c4e2816 29import org.eclipse.jdt.annotation.Nullable;
2bdf0193
AM
30import org.eclipse.tracecompass.internal.tmf.core.Activator;
31import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
32import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
33import org.eclipse.tracecompass.tmf.core.io.BufferedRandomAccessFile;
0bc16991
MAL
34import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
35import org.eclipse.tracecompass.tmf.core.signal.TmfTraceRangeUpdatedSignal;
2bdf0193
AM
36import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
37import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
2bdf0193 38import org.eclipse.tracecompass.tmf.core.trace.TmfTrace;
d26d67f5 39import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
2bdf0193
AM
40import org.eclipse.tracecompass.tmf.core.trace.TraceValidationStatus;
41import org.eclipse.tracecompass.tmf.core.trace.indexer.ITmfPersistentlyIndexable;
42import org.eclipse.tracecompass.tmf.core.trace.indexer.ITmfTraceIndexer;
43import org.eclipse.tracecompass.tmf.core.trace.indexer.TmfBTreeTraceIndexer;
44import org.eclipse.tracecompass.tmf.core.trace.indexer.checkpoint.ITmfCheckpoint;
45import org.eclipse.tracecompass.tmf.core.trace.indexer.checkpoint.TmfCheckpoint;
46import org.eclipse.tracecompass.tmf.core.trace.location.ITmfLocation;
47import org.eclipse.tracecompass.tmf.core.trace.location.TmfLongLocation;
bcb8c2cb
PT
48
49/**
50 * Extension of TmfTrace for handling of line-based text traces parsed using
51 * regular expressions. Each line that matches the first line pattern indicates
52 * the start of a new event. The subsequent lines can contain additional
53 * information that is added to the current event.
54 *
55 * @param <T>
56 * TmfEvent class returned by this trace
bcb8c2cb 57 */
5733be39 58public abstract class TextTrace<T extends TextTraceEvent> extends TmfTrace implements ITmfPersistentlyIndexable {
bcb8c2cb 59
661becf8 60 private static final TmfLongLocation NULL_LOCATION = new TmfLongLocation(-1L);
bcb8c2cb
PT
61 private static final int MAX_LINES = 100;
62 private static final int MAX_CONFIDENCE = 100;
63
64 /** The default separator used for multi-line fields */
65 protected static final String SEPARATOR = " | "; //$NON-NLS-1$
66
67 /** The text file */
68 protected BufferedRandomAccessFile fFile;
69
70 /**
71 * Constructor
72 */
73 public TextTrace() {
74 }
75
76 /**
77 * {@inheritDoc}
78 * <p>
543e2b1e
BH
79 * The default implementation computes the confidence as the sum of weighted
80 * values of the first 100 lines of the file which match any of the provided
81 * validation patterns. For each matching line a weighted value between 1.5
82 * and 2.0 is assigned based on the group count of the matching patterns.
83 * The higher the group count, the closer the weighted value will be to 2.0.
bcb8c2cb
PT
84 */
85 @Override
86 public IStatus validate(IProject project, String path) {
87 File file = new File(path);
88 if (!file.exists()) {
89 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "File not found: " + path); //$NON-NLS-1$
90 }
91 if (!file.isFile()) {
92 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Not a file. It's a directory: " + path); //$NON-NLS-1$
93 }
d26d67f5
BH
94 try {
95 if (!TmfTraceUtils.isText(file)) {
96 return new TraceValidationStatus(0, Activator.PLUGIN_ID);
97 }
98 } catch (IOException e) {
99 Activator.logError("Error validating file: " + path, e); //$NON-NLS-1$
100 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "IOException validating file: " + path, e); //$NON-NLS-1$
101 }
bcb8c2cb
PT
102 int confidence = 0;
103 try (BufferedRandomAccessFile rafile = new BufferedRandomAccessFile(path, "r")) { //$NON-NLS-1$
104 int lineCount = 0;
918551aa 105 double matches = 0.0;
bcb8c2cb 106 String line = rafile.getNextLine();
543e2b1e 107 List<Pattern> validationPatterns = getValidationPatterns();
bcb8c2cb 108 while ((line != null) && (lineCount++ < MAX_LINES)) {
6c230e50 109 line = preProcessLine(line);
543e2b1e
BH
110 for(Pattern pattern : validationPatterns) {
111 Matcher matcher = pattern.matcher(line);
112 if (matcher.matches()) {
113 int groupCount = matcher.groupCount();
114 matches += (1.0 + groupCount / ((double) groupCount + 1));
115 }
bcb8c2cb 116 }
918551aa 117 confidence = (int) (MAX_CONFIDENCE * matches / lineCount);
bcb8c2cb
PT
118 line = rafile.getNextLine();
119 }
120 } catch (IOException e) {
121 Activator.logError("Error validating file: " + path, e); //$NON-NLS-1$
122 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "IOException validating file: " + path, e); //$NON-NLS-1$
123 }
124
125 return new TraceValidationStatus(confidence, Activator.PLUGIN_ID);
126
127 }
bcb8c2cb
PT
128 @Override
129 public void initTrace(IResource resource, String path, Class<? extends ITmfEvent> type) throws TmfTraceException {
130 super.initTrace(resource, path, type);
0bc16991
MAL
131 initFile();
132 }
133
134 private void initFile() throws TmfTraceException {
135 closeFile();
bcb8c2cb
PT
136 try {
137 fFile = new BufferedRandomAccessFile(getPath(), "r"); //$NON-NLS-1$
138 } catch (IOException e) {
139 throw new TmfTraceException(e.getMessage(), e);
140 }
141 }
142
143 @Override
144 public synchronized void dispose() {
145 super.dispose();
0bc16991
MAL
146 closeFile();
147 }
148
149 private void closeFile() {
bcb8c2cb
PT
150 if (fFile != null) {
151 try {
152 fFile.close();
153 } catch (IOException e) {
154 } finally {
155 fFile = null;
156 }
157 }
158 }
159
160 @Override
5ece050b 161 public synchronized TextTraceContext seekEvent(ITmfLocation location) {
bcb8c2cb
PT
162 TextTraceContext context = new TextTraceContext(NULL_LOCATION, ITmfContext.UNKNOWN_RANK);
163 if (NULL_LOCATION.equals(location) || fFile == null) {
164 return context;
165 }
166 try {
167 if (location == null) {
168 fFile.seek(0);
169 } else if (location.getLocationInfo() instanceof Long) {
170 fFile.seek((Long) location.getLocationInfo());
171 }
172 long rawPos = fFile.getFilePointer();
173 String line = fFile.getNextLine();
174 while (line != null) {
6c230e50 175 line = preProcessLine(line);
bcb8c2cb 176 Matcher matcher = getFirstLinePattern().matcher(line);
13797536
MK
177 if (matcher.matches()) {
178 setupContext(context, rawPos, line, matcher);
bcb8c2cb
PT
179 return context;
180 }
181 rawPos = fFile.getFilePointer();
182 line = fFile.getNextLine();
183 }
184 return context;
185 } catch (IOException e) {
186 Activator.logError("Error seeking file: " + getPath(), e); //$NON-NLS-1$
187 return context;
188 }
189 }
190
13797536
MK
191 private void setupContext(TextTraceContext context, long rawPos, String line, Matcher matcher) throws IOException {
192 context.setLocation(new TmfLongLocation(rawPos));
193 context.firstLineMatcher = matcher;
194 context.firstLine = line;
195 context.nextLineLocation = fFile.getFilePointer();
196 }
197
bcb8c2cb 198 @Override
5ece050b 199 public synchronized TextTraceContext seekEvent(double ratio) {
bcb8c2cb
PT
200 if (fFile == null) {
201 return new TextTraceContext(NULL_LOCATION, ITmfContext.UNKNOWN_RANK);
202 }
203 try {
a0789537 204 long pos = Math.round(ratio * fFile.length());
bcb8c2cb
PT
205 while (pos > 0) {
206 fFile.seek(pos - 1);
207 if (fFile.read() == '\n') {
208 break;
209 }
210 pos--;
211 }
212 ITmfLocation location = new TmfLongLocation(Long.valueOf(pos));
5ece050b 213 TextTraceContext context = seekEvent(location);
bcb8c2cb
PT
214 context.setRank(ITmfContext.UNKNOWN_RANK);
215 return context;
216 } catch (IOException e) {
217 Activator.logError("Error seeking file: " + getPath(), e); //$NON-NLS-1$
218 return new TextTraceContext(NULL_LOCATION, ITmfContext.UNKNOWN_RANK);
219 }
220 }
221
222 @Override
223 public double getLocationRatio(ITmfLocation location) {
224 if (fFile == null) {
225 return 0;
226 }
227 try {
228 long length = fFile.length();
229 if (length == 0) {
230 return 0;
231 }
232 if (location.getLocationInfo() instanceof Long) {
233 return (double) ((Long) location.getLocationInfo()) / length;
234 }
235 } catch (IOException e) {
236 Activator.logError("Error reading file: " + getPath(), e); //$NON-NLS-1$
237 }
238 return 0;
239 }
240
241 @Override
242 public ITmfLocation getCurrentLocation() {
243 return null;
244 }
245
246 @Override
5ece050b
AM
247 public TextTraceEvent parseEvent(ITmfContext tmfContext) {
248 TextTraceContext context = seekEvent(tmfContext.getLocation());
bcb8c2cb
PT
249 return parse(context);
250 }
251
252 @Override
4c4e2816 253 public synchronized @Nullable T getNext(ITmfContext context) {
5ece050b
AM
254 if (!(context instanceof TextTraceContext)) {
255 throw new IllegalArgumentException();
256 }
257 TextTraceContext savedContext = new TextTraceContext(context.getLocation(), context.getRank());
4c4e2816 258 @Nullable T event = parse((TextTraceContext) context);
bcb8c2cb 259 if (event != null) {
5904c11e 260 updateAttributes(savedContext, event);
bcb8c2cb
PT
261 context.increaseRank();
262 }
263 return event;
264 }
265
266 /**
267 * Parse the next event. The context is advanced.
268 *
269 * @param tmfContext
270 * the context
271 * @return the next event or null
272 */
4c4e2816 273 protected synchronized @Nullable T parse(TextTraceContext tmfContext) {
bcb8c2cb
PT
274 if (fFile == null) {
275 return null;
276 }
5ece050b 277 TextTraceContext context = tmfContext;
38db0431
MK
278 ITmfLocation location = context.getLocation();
279 if (location == null || !(location.getLocationInfo() instanceof Long) || NULL_LOCATION.equals(location)) {
bcb8c2cb
PT
280 return null;
281 }
282
283 T event = parseFirstLine(context.firstLineMatcher, context.firstLine);
284
285 try {
286 if (fFile.getFilePointer() != context.nextLineLocation) {
287 fFile.seek(context.nextLineLocation);
288 }
289 long rawPos = fFile.getFilePointer();
290 String line = fFile.getNextLine();
291 while (line != null) {
6c230e50 292 line = preProcessLine(line);
bcb8c2cb 293 Matcher matcher = getFirstLinePattern().matcher(line);
13797536
MK
294 if (matcher.matches()) {
295 setupContext(context, rawPos, line, matcher);
bcb8c2cb
PT
296 return event;
297 }
298 parseNextLine(event, line);
299 rawPos = fFile.getFilePointer();
300 line = fFile.getNextLine();
301 }
302 } catch (IOException e) {
303 Activator.logError("Error reading file: " + getPath(), e); //$NON-NLS-1$
304 }
305
306 context.setLocation(NULL_LOCATION);
307 return event;
308 }
309
6c230e50
BH
310 /**
311 * Pre-processes the input line. The default implementation returns the
312 * input line.
313 *
314 * @param line
315 * non-null input string
316 * @return the pre-processed input line
317 */
318 @NonNull
319 protected String preProcessLine(@NonNull String line) {
320 return line;
321 }
322
bcb8c2cb
PT
323 /**
324 * Gets the first line pattern.
325 *
326 * @return The first line pattern
327 */
328 protected abstract Pattern getFirstLinePattern();
329
330 /**
6b44794a
MK
331 * Parses the first line data and returns a new event. When constructing the
332 * event, the concrete trace should use the trace's timestamp transform to
333 * create the timestamp, by either transforming the parsed time value
334 * directly or by using the method {@link #createTimestamp(long)}.
bcb8c2cb
PT
335 *
336 * @param matcher
337 * The matcher
338 * @param line
339 * The line to parse
340 * @return The parsed event
341 */
342 protected abstract T parseFirstLine(Matcher matcher, String line);
343
344 /**
345 * Parses the next line data for the current event.
346 *
347 * @param event
348 * The current event being parsed
349 * @param line
350 * The line to parse
351 */
352 protected abstract void parseNextLine(T event, String line);
353
543e2b1e
BH
354 /**
355 * Returns a ordered list of validation patterns that will be used in
356 * the default {@link #validate(IProject, String)} method to match
357 * the first 100 to compute the confidence level
358 *
359 * @return collection of patterns to validate against
543e2b1e
BH
360 */
361 protected List<Pattern> getValidationPatterns() {
362 return Collections.singletonList(getFirstLinePattern());
363 }
364
bcb8c2cb
PT
365 // ------------------------------------------------------------------------
366 // Helper methods
367 // ------------------------------------------------------------------------
368
369 /**
370 * Strip quotes surrounding a string
371 *
372 * @param input
373 * The input string
374 * @return The string without quotes
375 */
376 protected static String replaceQuotes(String input) {
377 String out = input.replaceAll("^\"|(\"\\s*)$", ""); //$NON-NLS-1$//$NON-NLS-2$
378 return out;
379 }
380
381 /**
382 * Strip brackets surrounding a string
383 *
384 * @param input
385 * The input string
386 * @return The string without brackets
387 */
388 protected static String replaceBrackets(String input) {
389 String out = input.replaceAll("^\\{|(\\}\\s*)$", ""); //$NON-NLS-1$//$NON-NLS-2$
390 return out;
391 }
392
738a2796
MAL
393 private static int fCheckpointSize = -1;
394
395 @Override
396 public synchronized int getCheckpointSize() {
397 if (fCheckpointSize == -1) {
398 TmfCheckpoint c = new TmfCheckpoint(TmfTimestamp.ZERO, new TmfLongLocation(0L), 0);
399 ByteBuffer b = ByteBuffer.allocate(ITmfCheckpoint.MAX_SERIALIZE_SIZE);
400 b.clear();
401 c.serialize(b);
402 fCheckpointSize = b.position();
403 }
404
405 return fCheckpointSize;
406 }
407
408 @Override
409 protected ITmfTraceIndexer createIndexer(int interval) {
410 return new TmfBTreeTraceIndexer(this, interval);
411 }
412
413 @Override
414 public ITmfLocation restoreLocation(ByteBuffer bufferIn) {
415 return new TmfLongLocation(bufferIn);
416 }
0bc16991
MAL
417
418 @TmfSignalHandler
419 @Override
420 public void traceRangeUpdated(TmfTraceRangeUpdatedSignal signal) {
421 if (signal.getTrace() == this) {
422 try {
423 synchronized (this) {
424 // Reset the file handle in case it has reached the end of the
425 // file already. Otherwise, it will not be able to read new data
426 // pass the previous end.
427 initFile();
428 }
429 } catch (TmfTraceException e) {
430 Activator.logError(e.getLocalizedMessage(), e);
431 }
432 }
433 super.traceRangeUpdated(signal);
434 }
bcb8c2cb 435}
This page took 0.095582 seconds and 5 git commands to generate.