tmf: Fix NPE for persitent experiment index when using text traces
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.core / src / org / eclipse / linuxtools / tmf / core / trace / text / TextTrace.java
1 /*******************************************************************************
2 * Copyright (c) 2012, 2014 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.linuxtools.tmf.core.trace.text;
15
16 import java.io.File;
17 import java.io.IOException;
18 import java.nio.ByteBuffer;
19 import java.util.regex.Matcher;
20 import java.util.regex.Pattern;
21
22 import org.eclipse.core.resources.IProject;
23 import org.eclipse.core.resources.IResource;
24 import org.eclipse.core.runtime.IStatus;
25 import org.eclipse.core.runtime.Status;
26 import org.eclipse.linuxtools.internal.tmf.core.Activator;
27 import org.eclipse.linuxtools.tmf.core.event.ITmfEvent;
28 import org.eclipse.linuxtools.tmf.core.event.TmfEvent;
29 import org.eclipse.linuxtools.tmf.core.exceptions.TmfTraceException;
30 import org.eclipse.linuxtools.tmf.core.io.BufferedRandomAccessFile;
31 import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestamp;
32 import org.eclipse.linuxtools.tmf.core.trace.ITmfContext;
33 import org.eclipse.linuxtools.tmf.core.trace.ITmfEventParser;
34 import org.eclipse.linuxtools.tmf.core.trace.TmfContext;
35 import org.eclipse.linuxtools.tmf.core.trace.TmfTrace;
36 import org.eclipse.linuxtools.tmf.core.trace.TraceValidationStatus;
37 import org.eclipse.linuxtools.tmf.core.trace.indexer.ITmfPersistentlyIndexable;
38 import org.eclipse.linuxtools.tmf.core.trace.indexer.ITmfTraceIndexer;
39 import org.eclipse.linuxtools.tmf.core.trace.indexer.TmfBTreeTraceIndexer;
40 import org.eclipse.linuxtools.tmf.core.trace.indexer.checkpoint.ITmfCheckpoint;
41 import org.eclipse.linuxtools.tmf.core.trace.indexer.checkpoint.TmfCheckpoint;
42 import org.eclipse.linuxtools.tmf.core.trace.location.ITmfLocation;
43 import org.eclipse.linuxtools.tmf.core.trace.location.TmfLongLocation;
44
45 /**
46 * Extension of TmfTrace for handling of line-based text traces parsed using
47 * regular expressions. Each line that matches the first line pattern indicates
48 * the start of a new event. The subsequent lines can contain additional
49 * information that is added to the current event.
50 *
51 * @param <T>
52 * TmfEvent class returned by this trace
53 *
54 * @since 3.0
55 */
56 public abstract class TextTrace<T extends TmfEvent> extends TmfTrace implements ITmfEventParser, ITmfPersistentlyIndexable {
57
58 private static final TmfLongLocation NULL_LOCATION = new TmfLongLocation(-1L);
59 private static final int MAX_LINES = 100;
60 private static final int MAX_CONFIDENCE = 100;
61
62 /** The default separator used for multi-line fields */
63 protected static final String SEPARATOR = " | "; //$NON-NLS-1$
64
65 /** The text file */
66 protected BufferedRandomAccessFile fFile;
67
68 /**
69 * Constructor
70 */
71 public TextTrace() {
72 }
73
74 /**
75 * {@inheritDoc}
76 * <p>
77 * The default implementation computes the confidence as the percentage of
78 * lines in the first 100 lines of the file which match the first line
79 * pattern.
80 */
81 @Override
82 public IStatus validate(IProject project, String path) {
83 File file = new File(path);
84 if (!file.exists()) {
85 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "File not found: " + path); //$NON-NLS-1$
86 }
87 if (!file.isFile()) {
88 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Not a file. It's a directory: " + path); //$NON-NLS-1$
89 }
90 int confidence = 0;
91 try (BufferedRandomAccessFile rafile = new BufferedRandomAccessFile(path, "r")) { //$NON-NLS-1$
92 int lineCount = 0;
93 int matches = 0;
94 String line = rafile.getNextLine();
95 while ((line != null) && (lineCount++ < MAX_LINES)) {
96 Matcher matcher = getFirstLinePattern().matcher(line);
97 if (matcher.matches()) {
98 matches++;
99 }
100 confidence = MAX_CONFIDENCE * matches / lineCount;
101 line = rafile.getNextLine();
102 }
103 } catch (IOException e) {
104 Activator.logError("Error validating file: " + path, e); //$NON-NLS-1$
105 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "IOException validating file: " + path, e); //$NON-NLS-1$
106 }
107
108 return new TraceValidationStatus(confidence, Activator.PLUGIN_ID);
109
110 }
111
112 @Override
113 public void initTrace(IResource resource, String path, Class<? extends ITmfEvent> type) throws TmfTraceException {
114 super.initTrace(resource, path, type);
115 try {
116 fFile = new BufferedRandomAccessFile(getPath(), "r"); //$NON-NLS-1$
117 } catch (IOException e) {
118 throw new TmfTraceException(e.getMessage(), e);
119 }
120 }
121
122 @Override
123 public synchronized void dispose() {
124 super.dispose();
125 if (fFile != null) {
126 try {
127 fFile.close();
128 } catch (IOException e) {
129 } finally {
130 fFile = null;
131 }
132 }
133 }
134
135 @Override
136 public synchronized TmfContext seekEvent(ITmfLocation location) {
137 TextTraceContext context = new TextTraceContext(NULL_LOCATION, ITmfContext.UNKNOWN_RANK);
138 if (NULL_LOCATION.equals(location) || fFile == null) {
139 return context;
140 }
141 try {
142 if (location == null) {
143 fFile.seek(0);
144 } else if (location.getLocationInfo() instanceof Long) {
145 fFile.seek((Long) location.getLocationInfo());
146 }
147 long rawPos = fFile.getFilePointer();
148 String line = fFile.getNextLine();
149 while (line != null) {
150 Matcher matcher = getFirstLinePattern().matcher(line);
151 if (matcher.matches()) {
152 setupContext(context, rawPos, line, matcher);
153 return context;
154 }
155 rawPos = fFile.getFilePointer();
156 line = fFile.getNextLine();
157 }
158 return context;
159 } catch (IOException e) {
160 Activator.logError("Error seeking file: " + getPath(), e); //$NON-NLS-1$
161 return context;
162 }
163 }
164
165 private void setupContext(TextTraceContext context, long rawPos, String line, Matcher matcher) throws IOException {
166 context.setLocation(new TmfLongLocation(rawPos));
167 context.firstLineMatcher = matcher;
168 context.firstLine = line;
169 context.nextLineLocation = fFile.getFilePointer();
170 }
171
172 @Override
173 public synchronized TmfContext seekEvent(double ratio) {
174 if (fFile == null) {
175 return new TextTraceContext(NULL_LOCATION, ITmfContext.UNKNOWN_RANK);
176 }
177 try {
178 long pos = (long) (ratio * fFile.length());
179 while (pos > 0) {
180 fFile.seek(pos - 1);
181 if (fFile.read() == '\n') {
182 break;
183 }
184 pos--;
185 }
186 ITmfLocation location = new TmfLongLocation(Long.valueOf(pos));
187 TmfContext context = seekEvent(location);
188 context.setRank(ITmfContext.UNKNOWN_RANK);
189 return context;
190 } catch (IOException e) {
191 Activator.logError("Error seeking file: " + getPath(), e); //$NON-NLS-1$
192 return new TextTraceContext(NULL_LOCATION, ITmfContext.UNKNOWN_RANK);
193 }
194 }
195
196 @Override
197 public double getLocationRatio(ITmfLocation location) {
198 if (fFile == null) {
199 return 0;
200 }
201 try {
202 long length = fFile.length();
203 if (length == 0) {
204 return 0;
205 }
206 if (location.getLocationInfo() instanceof Long) {
207 return (double) ((Long) location.getLocationInfo()) / length;
208 }
209 } catch (IOException e) {
210 Activator.logError("Error reading file: " + getPath(), e); //$NON-NLS-1$
211 }
212 return 0;
213 }
214
215 @Override
216 public ITmfLocation getCurrentLocation() {
217 return null;
218 }
219
220 @Override
221 public ITmfEvent parseEvent(ITmfContext tmfContext) {
222 ITmfContext context = seekEvent(tmfContext.getLocation());
223 return parse(context);
224 }
225
226 @Override
227 public synchronized T getNext(ITmfContext context) {
228 ITmfContext savedContext = new TmfContext(context.getLocation(), context.getRank());
229 T event = parse(context);
230 if (event != null) {
231 updateAttributes(savedContext, event.getTimestamp());
232 context.increaseRank();
233 }
234 return event;
235 }
236
237 /**
238 * Parse the next event. The context is advanced.
239 *
240 * @param tmfContext
241 * the context
242 * @return the next event or null
243 */
244 protected synchronized T parse(ITmfContext tmfContext) {
245 if (fFile == null) {
246 return null;
247 }
248 if (!(tmfContext instanceof TextTraceContext)) {
249 return null;
250 }
251 TextTraceContext context = (TextTraceContext) tmfContext;
252 if (context.getLocation() == null || !(context.getLocation().getLocationInfo() instanceof Long) || NULL_LOCATION.equals(context.getLocation())) {
253 return null;
254 }
255
256 T event = parseFirstLine(context.firstLineMatcher, context.firstLine);
257
258 try {
259 if (fFile.getFilePointer() != context.nextLineLocation) {
260 fFile.seek(context.nextLineLocation);
261 }
262 long rawPos = fFile.getFilePointer();
263 String line = fFile.getNextLine();
264 while (line != null) {
265 Matcher matcher = getFirstLinePattern().matcher(line);
266 if (matcher.matches()) {
267 setupContext(context, rawPos, line, matcher);
268 return event;
269 }
270 parseNextLine(event, line);
271 rawPos = fFile.getFilePointer();
272 line = fFile.getNextLine();
273 }
274 } catch (IOException e) {
275 Activator.logError("Error reading file: " + getPath(), e); //$NON-NLS-1$
276 }
277
278 context.setLocation(NULL_LOCATION);
279 return event;
280 }
281
282 /**
283 * Gets the first line pattern.
284 *
285 * @return The first line pattern
286 */
287 protected abstract Pattern getFirstLinePattern();
288
289 /**
290 * Parses the first line data and returns a new event.
291 *
292 * @param matcher
293 * The matcher
294 * @param line
295 * The line to parse
296 * @return The parsed event
297 */
298 protected abstract T parseFirstLine(Matcher matcher, String line);
299
300 /**
301 * Parses the next line data for the current event.
302 *
303 * @param event
304 * The current event being parsed
305 * @param line
306 * The line to parse
307 */
308 protected abstract void parseNextLine(T event, String line);
309
310 // ------------------------------------------------------------------------
311 // Helper methods
312 // ------------------------------------------------------------------------
313
314 /**
315 * Strip quotes surrounding a string
316 *
317 * @param input
318 * The input string
319 * @return The string without quotes
320 */
321 protected static String replaceQuotes(String input) {
322 String out = input.replaceAll("^\"|(\"\\s*)$", ""); //$NON-NLS-1$//$NON-NLS-2$
323 return out;
324 }
325
326 /**
327 * Strip brackets surrounding a string
328 *
329 * @param input
330 * The input string
331 * @return The string without brackets
332 */
333 protected static String replaceBrackets(String input) {
334 String out = input.replaceAll("^\\{|(\\}\\s*)$", ""); //$NON-NLS-1$//$NON-NLS-2$
335 return out;
336 }
337
338 private static int fCheckpointSize = -1;
339
340 @Override
341 public synchronized int getCheckpointSize() {
342 if (fCheckpointSize == -1) {
343 TmfCheckpoint c = new TmfCheckpoint(TmfTimestamp.ZERO, new TmfLongLocation(0L), 0);
344 ByteBuffer b = ByteBuffer.allocate(ITmfCheckpoint.MAX_SERIALIZE_SIZE);
345 b.clear();
346 c.serialize(b);
347 fCheckpointSize = b.position();
348 }
349
350 return fCheckpointSize;
351 }
352
353 @Override
354 protected ITmfTraceIndexer createIndexer(int interval) {
355 return new TmfBTreeTraceIndexer(this, interval);
356 }
357
358 @Override
359 public ITmfLocation restoreLocation(ByteBuffer bufferIn) {
360 return new TmfLongLocation(bufferIn);
361 }
362 }
This page took 0.045588 seconds and 6 git commands to generate.