8c13d91e489ab9247cbd9fcba77ae38bd2b37f0c
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.core / src / org / eclipse / tracecompass / tmf / core / trace / text / TextTrace.java
1 /*******************************************************************************
2 * Copyright (c) 2012, 2015 Ericsson
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
11 * Marc-Andre Laperle - Add persistent index support
12 *******************************************************************************/
13
14 package org.eclipse.tracecompass.tmf.core.trace.text;
15
16 import java.io.File;
17 import java.io.IOException;
18 import java.nio.ByteBuffer;
19 import java.util.Collections;
20 import java.util.List;
21 import java.util.regex.Matcher;
22 import java.util.regex.Pattern;
23
24 import org.eclipse.core.resources.IProject;
25 import org.eclipse.core.resources.IResource;
26 import org.eclipse.core.runtime.IStatus;
27 import org.eclipse.core.runtime.Status;
28 import org.eclipse.jdt.annotation.NonNull;
29 import org.eclipse.jdt.annotation.Nullable;
30 import org.eclipse.tracecompass.internal.tmf.core.Activator;
31 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
32 import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
33 import org.eclipse.tracecompass.tmf.core.io.BufferedRandomAccessFile;
34 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
35 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceRangeUpdatedSignal;
36 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
37 import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
38 import org.eclipse.tracecompass.tmf.core.trace.TmfTrace;
39 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
40 import org.eclipse.tracecompass.tmf.core.trace.TraceValidationStatus;
41 import org.eclipse.tracecompass.tmf.core.trace.indexer.ITmfPersistentlyIndexable;
42 import org.eclipse.tracecompass.tmf.core.trace.indexer.ITmfTraceIndexer;
43 import org.eclipse.tracecompass.tmf.core.trace.indexer.TmfBTreeTraceIndexer;
44 import org.eclipse.tracecompass.tmf.core.trace.indexer.checkpoint.ITmfCheckpoint;
45 import org.eclipse.tracecompass.tmf.core.trace.indexer.checkpoint.TmfCheckpoint;
46 import org.eclipse.tracecompass.tmf.core.trace.location.ITmfLocation;
47 import org.eclipse.tracecompass.tmf.core.trace.location.TmfLongLocation;
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
57 */
58 public abstract class TextTrace<T extends TextTraceEvent> extends TmfTrace implements ITmfPersistentlyIndexable {
59
60 private static final TmfLongLocation NULL_LOCATION = new TmfLongLocation(-1L);
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>
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.
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 }
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 }
102 int confidence = 0;
103 try (BufferedRandomAccessFile rafile = new BufferedRandomAccessFile(path, "r")) { //$NON-NLS-1$
104 int lineCount = 0;
105 double matches = 0.0;
106 String line = rafile.getNextLine();
107 List<Pattern> validationPatterns = getValidationPatterns();
108 while ((line != null) && (lineCount++ < MAX_LINES)) {
109 line = preProcessLine(line);
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 }
116 }
117 confidence = (int) (MAX_CONFIDENCE * matches / lineCount);
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 }
128 @Override
129 public void initTrace(IResource resource, String path, Class<? extends ITmfEvent> type) throws TmfTraceException {
130 super.initTrace(resource, path, type);
131 initFile();
132 }
133
134 private void initFile() throws TmfTraceException {
135 closeFile();
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();
146 closeFile();
147 }
148
149 private void closeFile() {
150 if (fFile != null) {
151 try {
152 fFile.close();
153 } catch (IOException e) {
154 } finally {
155 fFile = null;
156 }
157 }
158 }
159
160 @Override
161 public synchronized TextTraceContext seekEvent(ITmfLocation location) {
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) {
175 line = preProcessLine(line);
176 Matcher matcher = getFirstLinePattern().matcher(line);
177 if (matcher.matches()) {
178 setupContext(context, rawPos, line, matcher);
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
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
198 @Override
199 public synchronized TextTraceContext seekEvent(double ratio) {
200 if (fFile == null) {
201 return new TextTraceContext(NULL_LOCATION, ITmfContext.UNKNOWN_RANK);
202 }
203 try {
204 long pos = Math.round(ratio * fFile.length());
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));
213 TextTraceContext context = seekEvent(location);
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
247 public TextTraceEvent parseEvent(ITmfContext tmfContext) {
248 TextTraceContext context = seekEvent(tmfContext.getLocation());
249 return parse(context);
250 }
251
252 @Override
253 public synchronized @Nullable T getNext(ITmfContext context) {
254 if (!(context instanceof TextTraceContext)) {
255 throw new IllegalArgumentException();
256 }
257 TextTraceContext savedContext = new TextTraceContext(context.getLocation(), context.getRank());
258 @Nullable T event = parse((TextTraceContext) context);
259 if (event != null) {
260 updateAttributes(savedContext, event);
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 */
273 protected synchronized @Nullable T parse(TextTraceContext tmfContext) {
274 if (fFile == null) {
275 return null;
276 }
277 TextTraceContext context = tmfContext;
278 if (context.getLocation() == null || !(context.getLocation().getLocationInfo() instanceof Long) || NULL_LOCATION.equals(context.getLocation())) {
279 return null;
280 }
281
282 T event = parseFirstLine(context.firstLineMatcher, context.firstLine);
283
284 try {
285 if (fFile.getFilePointer() != context.nextLineLocation) {
286 fFile.seek(context.nextLineLocation);
287 }
288 long rawPos = fFile.getFilePointer();
289 String line = fFile.getNextLine();
290 while (line != null) {
291 line = preProcessLine(line);
292 Matcher matcher = getFirstLinePattern().matcher(line);
293 if (matcher.matches()) {
294 setupContext(context, rawPos, line, matcher);
295 return event;
296 }
297 parseNextLine(event, line);
298 rawPos = fFile.getFilePointer();
299 line = fFile.getNextLine();
300 }
301 } catch (IOException e) {
302 Activator.logError("Error reading file: " + getPath(), e); //$NON-NLS-1$
303 }
304
305 context.setLocation(NULL_LOCATION);
306 return event;
307 }
308
309 /**
310 * Pre-processes the input line. The default implementation returns the
311 * input line.
312 *
313 * @param line
314 * non-null input string
315 * @return the pre-processed input line
316 */
317 @NonNull
318 protected String preProcessLine(@NonNull String line) {
319 return line;
320 }
321
322 /**
323 * Gets the first line pattern.
324 *
325 * @return The first line pattern
326 */
327 protected abstract Pattern getFirstLinePattern();
328
329 /**
330 * Parses the first line data and returns a new event. When constructing the
331 * event, the concrete trace should use the trace's timestamp transform to
332 * create the timestamp, by either transforming the parsed time value
333 * directly or by using the method {@link #createTimestamp(long)}.
334 *
335 * @param matcher
336 * The matcher
337 * @param line
338 * The line to parse
339 * @return The parsed event
340 */
341 protected abstract T parseFirstLine(Matcher matcher, String line);
342
343 /**
344 * Parses the next line data for the current event.
345 *
346 * @param event
347 * The current event being parsed
348 * @param line
349 * The line to parse
350 */
351 protected abstract void parseNextLine(T event, String line);
352
353 /**
354 * Returns a ordered list of validation patterns that will be used in
355 * the default {@link #validate(IProject, String)} method to match
356 * the first 100 to compute the confidence level
357 *
358 * @return collection of patterns to validate against
359 */
360 protected List<Pattern> getValidationPatterns() {
361 return Collections.singletonList(getFirstLinePattern());
362 }
363
364 // ------------------------------------------------------------------------
365 // Helper methods
366 // ------------------------------------------------------------------------
367
368 /**
369 * Strip quotes surrounding a string
370 *
371 * @param input
372 * The input string
373 * @return The string without quotes
374 */
375 protected static String replaceQuotes(String input) {
376 String out = input.replaceAll("^\"|(\"\\s*)$", ""); //$NON-NLS-1$//$NON-NLS-2$
377 return out;
378 }
379
380 /**
381 * Strip brackets surrounding a string
382 *
383 * @param input
384 * The input string
385 * @return The string without brackets
386 */
387 protected static String replaceBrackets(String input) {
388 String out = input.replaceAll("^\\{|(\\}\\s*)$", ""); //$NON-NLS-1$//$NON-NLS-2$
389 return out;
390 }
391
392 private static int fCheckpointSize = -1;
393
394 @Override
395 public synchronized int getCheckpointSize() {
396 if (fCheckpointSize == -1) {
397 TmfCheckpoint c = new TmfCheckpoint(TmfTimestamp.ZERO, new TmfLongLocation(0L), 0);
398 ByteBuffer b = ByteBuffer.allocate(ITmfCheckpoint.MAX_SERIALIZE_SIZE);
399 b.clear();
400 c.serialize(b);
401 fCheckpointSize = b.position();
402 }
403
404 return fCheckpointSize;
405 }
406
407 @Override
408 protected ITmfTraceIndexer createIndexer(int interval) {
409 return new TmfBTreeTraceIndexer(this, interval);
410 }
411
412 @Override
413 public ITmfLocation restoreLocation(ByteBuffer bufferIn) {
414 return new TmfLongLocation(bufferIn);
415 }
416
417 @TmfSignalHandler
418 @Override
419 public void traceRangeUpdated(TmfTraceRangeUpdatedSignal signal) {
420 if (signal.getTrace() == this) {
421 try {
422 synchronized (this) {
423 // Reset the file handle in case it has reached the end of the
424 // file already. Otherwise, it will not be able to read new data
425 // pass the previous end.
426 initFile();
427 }
428 } catch (TmfTraceException e) {
429 Activator.logError(e.getLocalizedMessage(), e);
430 }
431 }
432 super.traceRangeUpdated(signal);
433 }
434 }
This page took 0.045381 seconds and 4 git commands to generate.