1 /*******************************************************************************
2 * Copyright (c) 2010, 2014 Ericsson
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
10 * Patrick Tasse - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.tracecompass
.tmf
.core
.parsers
.custom
;
16 import java
.io
.FileNotFoundException
;
17 import java
.io
.IOException
;
18 import java
.nio
.ByteBuffer
;
19 import java
.util
.HashMap
;
20 import java
.util
.Iterator
;
21 import java
.util
.List
;
22 import java
.util
.Map
.Entry
;
23 import java
.util
.regex
.Matcher
;
25 import org
.eclipse
.core
.resources
.IProject
;
26 import org
.eclipse
.core
.resources
.IResource
;
27 import org
.eclipse
.core
.runtime
.IStatus
;
28 import org
.eclipse
.core
.runtime
.Status
;
29 import org
.eclipse
.tracecompass
.internal
.tmf
.core
.Activator
;
30 import org
.eclipse
.tracecompass
.tmf
.core
.event
.ITmfEvent
;
31 import org
.eclipse
.tracecompass
.tmf
.core
.exceptions
.TmfTraceException
;
32 import org
.eclipse
.tracecompass
.tmf
.core
.io
.BufferedRandomAccessFile
;
33 import org
.eclipse
.tracecompass
.tmf
.core
.parsers
.custom
.CustomTxtTraceDefinition
.InputLine
;
34 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.TmfTimestamp
;
35 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfContext
;
36 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfEventParser
;
37 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfContext
;
38 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTrace
;
39 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TraceValidationStatus
;
40 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.indexer
.ITmfPersistentlyIndexable
;
41 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.indexer
.ITmfTraceIndexer
;
42 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.indexer
.TmfBTreeTraceIndexer
;
43 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.indexer
.checkpoint
.ITmfCheckpoint
;
44 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.indexer
.checkpoint
.TmfCheckpoint
;
45 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.location
.ITmfLocation
;
46 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.location
.TmfLongLocation
;
49 * Base class for custom plain text traces.
51 * @author Patrick Tassé
54 public class CustomTxtTrace
extends TmfTrace
implements ITmfEventParser
, ITmfPersistentlyIndexable
{
56 private static final TmfLongLocation NULL_LOCATION
= new TmfLongLocation(-1L);
57 private static final int DEFAULT_CACHE_SIZE
= 100;
58 private static final int MAX_LINES
= 100;
59 private static final int MAX_CONFIDENCE
= 100;
61 private final CustomTxtTraceDefinition fDefinition
;
62 private final CustomTxtEventType fEventType
;
63 private BufferedRandomAccessFile fFile
;
69 * Text trace definition
71 public CustomTxtTrace(final CustomTxtTraceDefinition definition
) {
72 fDefinition
= definition
;
73 fEventType
= new CustomTxtEventType(fDefinition
);
74 setCacheSize(DEFAULT_CACHE_SIZE
);
83 * Text trace definition
85 * Path to the trace file
88 * @throws TmfTraceException
89 * If we couldn't open the trace at 'path'
91 public CustomTxtTrace(final IResource resource
,
92 final CustomTxtTraceDefinition definition
, final String path
,
93 final int cacheSize
) throws TmfTraceException
{
95 setCacheSize((cacheSize
> 0) ? cacheSize
: DEFAULT_CACHE_SIZE
);
96 initTrace(resource
, path
, CustomTxtEvent
.class);
100 public void initTrace(final IResource resource
, final String path
, final Class
<?
extends ITmfEvent
> eventType
) throws TmfTraceException
{
101 super.initTrace(resource
, path
, eventType
);
103 fFile
= new BufferedRandomAccessFile(getPath(), "r"); //$NON-NLS-1$
104 } catch (IOException e
) {
105 throw new TmfTraceException(e
.getMessage(), e
);
110 public synchronized void dispose() {
115 } catch (IOException e
) {
123 public ITmfTraceIndexer
getIndexer() {
124 return super.getIndexer();
128 public synchronized TmfContext
seekEvent(final ITmfLocation location
) {
129 final CustomTxtTraceContext context
= new CustomTxtTraceContext(NULL_LOCATION
, ITmfContext
.UNKNOWN_RANK
);
130 if (NULL_LOCATION
.equals(location
) || fFile
== null) {
134 if (location
== null) {
136 } else if (location
.getLocationInfo() instanceof Long
) {
137 fFile
.seek((Long
) location
.getLocationInfo());
139 long rawPos
= fFile
.getFilePointer();
140 String line
= fFile
.getNextLine();
141 while (line
!= null) {
142 for (final InputLine input
: getFirstLines()) {
143 final Matcher matcher
= input
.getPattern().matcher(line
);
144 if (matcher
.matches()) {
145 context
.setLocation(new TmfLongLocation(rawPos
));
146 context
.firstLineMatcher
= matcher
;
147 context
.firstLine
= line
;
148 context
.nextLineLocation
= fFile
.getFilePointer();
149 context
.inputLine
= input
;
153 rawPos
= fFile
.getFilePointer();
154 line
= fFile
.getNextLine();
157 } catch (final FileNotFoundException e
) {
158 Activator
.logError("Error seeking event. File not found: " + getPath(), e
); //$NON-NLS-1$
160 } catch (final IOException e
) {
161 Activator
.logError("Error seeking event. File: " + getPath(), e
); //$NON-NLS-1$
168 public synchronized TmfContext
seekEvent(final double ratio
) {
170 return new CustomTxtTraceContext(NULL_LOCATION
, ITmfContext
.UNKNOWN_RANK
);
173 long pos
= Math
.round(ratio
* fFile
.length());
176 if (fFile
.read() == '\n') {
181 final ITmfLocation location
= new TmfLongLocation(pos
);
182 final TmfContext context
= seekEvent(location
);
183 context
.setRank(ITmfContext
.UNKNOWN_RANK
);
185 } catch (final IOException e
) {
186 Activator
.logError("Error seeking event. File: " + getPath(), e
); //$NON-NLS-1$
187 return new CustomTxtTraceContext(NULL_LOCATION
, ITmfContext
.UNKNOWN_RANK
);
192 public synchronized double getLocationRatio(final ITmfLocation location
) {
197 if (location
.getLocationInfo() instanceof Long
) {
198 return ((Long
) location
.getLocationInfo()).doubleValue() / fFile
.length();
200 } catch (final IOException e
) {
201 Activator
.logError("Error seeking event. File: " + getPath(), e
); //$NON-NLS-1$
207 public ITmfLocation
getCurrentLocation() {
208 // TODO Auto-generated method stub
213 public synchronized CustomTxtEvent
parseEvent(final ITmfContext tmfContext
) {
214 ITmfContext context
= seekEvent(tmfContext
.getLocation());
215 return parse(context
);
219 public synchronized CustomTxtEvent
getNext(final ITmfContext context
) {
220 final ITmfContext savedContext
= new TmfContext(context
.getLocation(), context
.getRank());
221 final CustomTxtEvent event
= parse(context
);
223 updateAttributes(savedContext
, event
.getTimestamp());
224 context
.increaseRank();
229 private synchronized CustomTxtEvent
parse(final ITmfContext tmfContext
) {
233 if (!(tmfContext
instanceof CustomTxtTraceContext
)) {
237 final CustomTxtTraceContext context
= (CustomTxtTraceContext
) tmfContext
;
238 if (context
.getLocation() == null || !(context
.getLocation().getLocationInfo() instanceof Long
) || NULL_LOCATION
.equals(context
.getLocation())) {
242 CustomTxtEvent event
= parseFirstLine(context
);
244 final HashMap
<InputLine
, Integer
> countMap
= new HashMap
<>();
245 InputLine currentInput
= null;
246 if (context
.inputLine
.childrenInputs
!= null && context
.inputLine
.childrenInputs
.size() > 0) {
247 currentInput
= context
.inputLine
.childrenInputs
.get(0);
248 countMap
.put(currentInput
, 0);
252 if (fFile
.getFilePointer() != context
.nextLineLocation
) {
253 fFile
.seek(context
.nextLineLocation
);
255 long rawPos
= fFile
.getFilePointer();
256 String line
= fFile
.getNextLine();
257 while (line
!= null) {
258 boolean processed
= false;
259 if (currentInput
== null) {
260 for (final InputLine input
: getFirstLines()) {
261 final Matcher matcher
= input
.getPattern().matcher(line
);
262 if (matcher
.matches()) {
263 context
.setLocation(new TmfLongLocation(rawPos
));
264 context
.firstLineMatcher
= matcher
;
265 context
.firstLine
= line
;
266 context
.nextLineLocation
= fFile
.getFilePointer();
267 context
.inputLine
= input
;
272 if (countMap
.get(currentInput
) >= currentInput
.getMinCount()) {
273 final List
<InputLine
> nextInputs
= currentInput
.getNextInputs(countMap
);
274 if (nextInputs
.size() == 0 || nextInputs
.get(nextInputs
.size() - 1).getMinCount() == 0) {
275 for (final InputLine input
: getFirstLines()) {
276 final Matcher matcher
= input
.getPattern().matcher(line
);
277 if (matcher
.matches()) {
278 context
.setLocation(new TmfLongLocation(rawPos
));
279 context
.firstLineMatcher
= matcher
;
280 context
.firstLine
= line
;
281 context
.nextLineLocation
= fFile
.getFilePointer();
282 context
.inputLine
= input
;
287 for (final InputLine input
: nextInputs
) {
288 final Matcher matcher
= input
.getPattern().matcher(line
);
289 if (matcher
.matches()) {
290 event
.processGroups(input
, matcher
);
291 currentInput
= input
;
292 if (countMap
.get(currentInput
) == null) {
293 countMap
.put(currentInput
, 1);
295 countMap
.put(currentInput
, countMap
.get(currentInput
) + 1);
297 Iterator
<InputLine
> iter
= countMap
.keySet().iterator();
298 while (iter
.hasNext()) {
299 final InputLine inputLine
= iter
.next();
300 if (inputLine
.level
> currentInput
.level
) {
304 if (currentInput
.childrenInputs
!= null && currentInput
.childrenInputs
.size() > 0) {
305 currentInput
= currentInput
.childrenInputs
.get(0);
306 countMap
.put(currentInput
, 0);
307 } else if (countMap
.get(currentInput
) >= currentInput
.getMaxCount()) {
308 if (currentInput
.getNextInputs(countMap
).size() > 0) {
309 currentInput
= currentInput
.getNextInputs(countMap
).get(0);
310 if (countMap
.get(currentInput
) == null) {
311 countMap
.put(currentInput
, 0);
313 iter
= countMap
.keySet().iterator();
314 while (iter
.hasNext()) {
315 final InputLine inputLine
= iter
.next();
316 if (inputLine
.level
> currentInput
.level
) {
329 if (!processed
&& currentInput
!= null) {
330 final Matcher matcher
= currentInput
.getPattern().matcher(line
);
331 if (matcher
.matches()) {
332 event
.processGroups(currentInput
, matcher
);
333 countMap
.put(currentInput
, countMap
.get(currentInput
) + 1);
334 if (currentInput
.childrenInputs
!= null && currentInput
.childrenInputs
.size() > 0) {
335 currentInput
= currentInput
.childrenInputs
.get(0);
336 countMap
.put(currentInput
, 0);
337 } else if (countMap
.get(currentInput
) >= currentInput
.getMaxCount()) {
338 if (currentInput
.getNextInputs(countMap
).size() > 0) {
339 currentInput
= currentInput
.getNextInputs(countMap
).get(0);
340 if (countMap
.get(currentInput
) == null) {
341 countMap
.put(currentInput
, 0);
343 final Iterator
<InputLine
> iter
= countMap
.keySet().iterator();
344 while (iter
.hasNext()) {
345 final InputLine inputLine
= iter
.next();
346 if (inputLine
.level
> currentInput
.level
) {
355 ((StringBuffer
) event
.getContent().getValue()).append("\n").append(line
); //$NON-NLS-1$
358 rawPos
= fFile
.getFilePointer();
359 line
= fFile
.getNextLine();
361 } catch (final IOException e
) {
362 Activator
.logError("Error seeking event. File: " + getPath(), e
); //$NON-NLS-1$
364 for (final Entry
<InputLine
, Integer
> entry
: countMap
.entrySet()) {
365 if (entry
.getValue() < entry
.getKey().getMinCount()) {
369 context
.setLocation(NULL_LOCATION
);
374 * @return The first few lines of the text file
376 public List
<InputLine
> getFirstLines() {
377 return fDefinition
.inputs
;
381 * Parse the first line of the trace (to recognize the type).
385 * @return The first event
387 public CustomTxtEvent
parseFirstLine(final CustomTxtTraceContext context
) {
388 final CustomTxtEvent event
= new CustomTxtEvent(fDefinition
, this, TmfTimestamp
.ZERO
, "", fEventType
, ""); //$NON-NLS-1$ //$NON-NLS-2$
389 event
.processGroups(context
.inputLine
, context
.firstLineMatcher
);
390 event
.setContent(new CustomEventContent(event
, new StringBuffer(context
.firstLine
)));
395 * Get the trace definition.
397 * @return The trace definition
399 public CustomTraceDefinition
getDefinition() {
406 * The default implementation computes the confidence as the percentage of
407 * lines in the first 100 lines of the file which match any of the root
408 * input line patterns.
411 public IStatus
validate(IProject project
, String path
) {
412 File file
= new File(path
);
413 if (!file
.exists() || !file
.isFile() || !file
.canRead()) {
414 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, Messages
.CustomTrace_FileNotFound
+ ": " + path
); //$NON-NLS-1$
417 try (BufferedRandomAccessFile rafile
= new BufferedRandomAccessFile(path
, "r")) { //$NON-NLS-1$
419 double matches
= 0.0;
420 String line
= rafile
.getNextLine();
421 while ((line
!= null) && (lineCount
++ < MAX_LINES
)) {
422 for (InputLine inputLine
: fDefinition
.inputs
) {
423 Matcher matcher
= inputLine
.getPattern().matcher(line
);
424 if (matcher
.matches()) {
425 int groupCount
= matcher
.groupCount();
426 matches
+= (1.0 + groupCount
/ ((double) groupCount
+ 1));
430 confidence
= (int) (MAX_CONFIDENCE
* matches
/ lineCount
);
431 line
= rafile
.getNextLine();
433 } catch (IOException e
) {
434 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, "IOException validating file: " + path
, e
); //$NON-NLS-1$
436 return new TraceValidationStatus(confidence
, Activator
.PLUGIN_ID
);
439 private static int fCheckpointSize
= -1;
442 public synchronized int getCheckpointSize() {
443 if (fCheckpointSize
== -1) {
444 TmfCheckpoint c
= new TmfCheckpoint(TmfTimestamp
.ZERO
, new TmfLongLocation(0L), 0);
445 ByteBuffer b
= ByteBuffer
.allocate(ITmfCheckpoint
.MAX_SERIALIZE_SIZE
);
448 fCheckpointSize
= b
.position();
451 return fCheckpointSize
;
455 public ITmfLocation
restoreLocation(ByteBuffer bufferIn
) {
456 return new TmfLongLocation(bufferIn
);
460 protected ITmfTraceIndexer
createIndexer(int interval
) {
461 return new TmfBTreeTraceIndexer(this, interval
);