tmf: Replace TmfFilterEventTypeNode with TmfFilterTraceTypeNode
[deliverable/tracecompass.git] / org.eclipse.tracecompass.tmf.core / src / org / eclipse / tracecompass / tmf / core / parsers / custom / CustomXmlTrace.java
CommitLineData
6151d86c 1/*******************************************************************************
60ae41e1 2 * Copyright (c) 2010, 2014 Ericsson
6151d86c
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
11 *******************************************************************************/
12
2bdf0193 13package org.eclipse.tracecompass.tmf.core.parsers.custom;
6151d86c
PT
14
15import java.io.ByteArrayInputStream;
eb8ea213 16import java.io.File;
6151d86c
PT
17import java.io.IOException;
18import java.io.RandomAccessFile;
032ecd45 19import java.nio.ByteBuffer;
6151d86c
PT
20
21import javax.xml.parsers.DocumentBuilder;
22import javax.xml.parsers.DocumentBuilderFactory;
23import javax.xml.parsers.ParserConfigurationException;
eb8ea213 24
6151d86c
PT
25import org.eclipse.core.resources.IProject;
26import org.eclipse.core.resources.IResource;
a3db8436
AM
27import org.eclipse.core.runtime.IStatus;
28import org.eclipse.core.runtime.Status;
2bdf0193 29import org.eclipse.tracecompass.internal.tmf.core.Activator;
b04903a2 30import org.eclipse.tracecompass.internal.tmf.core.parsers.custom.CustomEventAspects;
2bdf0193 31import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
b04903a2 32import org.eclipse.tracecompass.tmf.core.event.aspect.ITmfEventAspect;
2bdf0193
AM
33import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
34import org.eclipse.tracecompass.tmf.core.io.BufferedRandomAccessFile;
2bdf0193
AM
35import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
36import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
2bdf0193 37import org.eclipse.tracecompass.tmf.core.trace.TmfContext;
2bdf0193
AM
38import org.eclipse.tracecompass.tmf.core.trace.TraceValidationStatus;
39import org.eclipse.tracecompass.tmf.core.trace.indexer.ITmfPersistentlyIndexable;
40import org.eclipse.tracecompass.tmf.core.trace.indexer.ITmfTraceIndexer;
41import org.eclipse.tracecompass.tmf.core.trace.indexer.TmfBTreeTraceIndexer;
42import org.eclipse.tracecompass.tmf.core.trace.indexer.checkpoint.ITmfCheckpoint;
43import org.eclipse.tracecompass.tmf.core.trace.indexer.checkpoint.TmfCheckpoint;
44import org.eclipse.tracecompass.tmf.core.trace.location.ITmfLocation;
45import org.eclipse.tracecompass.tmf.core.trace.location.TmfLongLocation;
6151d86c
PT
46import org.w3c.dom.Document;
47import org.w3c.dom.Element;
48import org.w3c.dom.Node;
49import org.w3c.dom.NodeList;
50import org.xml.sax.EntityResolver;
51import org.xml.sax.ErrorHandler;
52import org.xml.sax.InputSource;
53import org.xml.sax.SAXException;
54import org.xml.sax.SAXParseException;
55
a0a88f65
AM
56/**
57 * Trace object for custom XML trace parsers.
58 *
59 * @author Patrick Tassé
47aafe74 60 * @since 3.0
a0a88f65 61 */
2b0005f0 62public class CustomXmlTrace extends CustomTrace implements ITmfPersistentlyIndexable {
6151d86c 63
661becf8 64 private static final TmfLongLocation NULL_LOCATION = new TmfLongLocation(-1L);
6151d86c 65 private static final int DEFAULT_CACHE_SIZE = 100;
cd43d683
PT
66 private static final int MAX_LINES = 100;
67 private static final int CONFIDENCE = 100;
6151d86c
PT
68
69 private final CustomXmlTraceDefinition fDefinition;
70 private final CustomXmlEventType fEventType;
a7418109 71 private final CustomXmlInputElement fRecordInputElement;
6151d86c
PT
72 private BufferedRandomAccessFile fFile;
73
a0a88f65
AM
74 /**
75 * Basic constructor
76 *
eb8ea213
MK
77 * @param definition
78 * Trace definition
a0a88f65 79 */
6151d86c 80 public CustomXmlTrace(final CustomXmlTraceDefinition definition) {
2b0005f0 81 super(definition);
6151d86c
PT
82 fDefinition = definition;
83 fEventType = new CustomXmlEventType(fDefinition);
84 fRecordInputElement = getRecordInputElement(fDefinition.rootInputElement);
85 setCacheSize(DEFAULT_CACHE_SIZE);
86 }
87
a0a88f65
AM
88 /**
89 * Full constructor
90 *
91 * @param resource
92 * Trace resource
93 * @param definition
94 * Trace definition
95 * @param path
96 * Path to the trace/log file
97 * @param pageSize
98 * Page size to use
99 * @throws TmfTraceException
100 * If the trace/log couldn't be opened
101 */
102 public CustomXmlTrace(final IResource resource,
103 final CustomXmlTraceDefinition definition, final String path,
104 final int pageSize) throws TmfTraceException {
6151d86c
PT
105 this(definition);
106 setCacheSize((pageSize > 0) ? pageSize : DEFAULT_CACHE_SIZE);
107 initTrace(resource, path, CustomXmlEvent.class);
108 }
109
110 @Override
111 public void initTrace(final IResource resource, final String path, final Class<? extends ITmfEvent> eventType) throws TmfTraceException {
112 super.initTrace(resource, path, eventType);
113 try {
114 fFile = new BufferedRandomAccessFile(getPath(), "r"); //$NON-NLS-1$
115 } catch (IOException e) {
116 throw new TmfTraceException(e.getMessage(), e);
117 }
6151d86c
PT
118 }
119
120 @Override
121 public synchronized void dispose() {
122 super.dispose();
123 if (fFile != null) {
124 try {
125 fFile.close();
126 } catch (IOException e) {
127 } finally {
128 fFile = null;
129 }
130 }
131 }
132
b0422293
PT
133 @Override
134 public ITmfTraceIndexer getIndexer() {
135 return super.getIndexer();
136 }
137
b04903a2
AM
138 @Override
139 public Iterable<ITmfEventAspect> getEventAspects() {
140 return CustomEventAspects.generateAspects(fDefinition);
141 }
142
6151d86c
PT
143 @Override
144 public synchronized TmfContext seekEvent(final ITmfLocation location) {
145 final CustomXmlTraceContext context = new CustomXmlTraceContext(NULL_LOCATION, ITmfContext.UNKNOWN_RANK);
146 if (NULL_LOCATION.equals(location) || fFile == null) {
147 return context;
148 }
149 try {
150 if (location == null) {
151 fFile.seek(0);
152 } else if (location.getLocationInfo() instanceof Long) {
153 fFile.seek((Long) location.getLocationInfo());
154 }
6151d86c 155 long rawPos = fFile.getFilePointer();
cd43d683
PT
156 String line = fFile.getNextLine();
157 while (line != null) {
a7418109 158 final int idx = indexOfElement(fRecordInputElement.getElementName(), line, 0);
6151d86c
PT
159 if (idx != -1) {
160 context.setLocation(new TmfLongLocation(rawPos + idx));
161 return context;
162 }
163 rawPos = fFile.getFilePointer();
cd43d683 164 line = fFile.getNextLine();
6151d86c
PT
165 }
166 return context;
167 } catch (final IOException e) {
47aafe74 168 Activator.logError("Error seeking event. File: " + getPath(), e); //$NON-NLS-1$
6151d86c
PT
169 return context;
170 }
171
172 }
173
174 @Override
175 public synchronized TmfContext seekEvent(final double ratio) {
176 if (fFile == null) {
177 return new CustomTxtTraceContext(NULL_LOCATION, ITmfContext.UNKNOWN_RANK);
178 }
179 try {
91f6e587 180 long pos = Math.round(ratio * fFile.length());
6151d86c
PT
181 while (pos > 0) {
182 fFile.seek(pos - 1);
183 if (fFile.read() == '\n') {
184 break;
185 }
186 pos--;
187 }
188 final ITmfLocation location = new TmfLongLocation(pos);
189 final TmfContext context = seekEvent(location);
190 context.setRank(ITmfContext.UNKNOWN_RANK);
191 return context;
192 } catch (final IOException e) {
47aafe74 193 Activator.logError("Error seeking event. File: " + getPath(), e); //$NON-NLS-1$
6151d86c
PT
194 return new CustomXmlTraceContext(NULL_LOCATION, ITmfContext.UNKNOWN_RANK);
195 }
196 }
197
198 @Override
199 public synchronized double getLocationRatio(final ITmfLocation location) {
200 if (fFile == null) {
201 return 0;
202 }
203 try {
204 if (location.getLocationInfo() instanceof Long) {
0126a8ca 205 return ((Long) location.getLocationInfo()).doubleValue() / fFile.length();
6151d86c
PT
206 }
207 } catch (final IOException e) {
47aafe74 208 Activator.logError("Error getting location ration. File: " + getPath(), e); //$NON-NLS-1$
6151d86c
PT
209 }
210 return 0;
211 }
212
213 @Override
214 public ITmfLocation getCurrentLocation() {
215 // TODO Auto-generated method stub
216 return null;
217 }
218
219 @Override
220 public synchronized CustomXmlEvent parseEvent(final ITmfContext tmfContext) {
221 ITmfContext context = seekEvent(tmfContext.getLocation());
222 return parse(context);
223 }
224
225 @Override
226 public synchronized CustomXmlEvent getNext(final ITmfContext context) {
4c9f2944 227 final ITmfContext savedContext = new TmfContext(context.getLocation(), context.getRank());
6151d86c
PT
228 final CustomXmlEvent event = parse(context);
229 if (event != null) {
230 updateAttributes(savedContext, event.getTimestamp());
231 context.increaseRank();
232 }
233 return event;
234 }
235
236 private synchronized CustomXmlEvent parse(final ITmfContext tmfContext) {
237 if (fFile == null) {
238 return null;
239 }
240 if (!(tmfContext instanceof CustomXmlTraceContext)) {
241 return null;
242 }
243
244 final CustomXmlTraceContext context = (CustomXmlTraceContext) tmfContext;
9cbe7899 245 if (context.getLocation() == null || !(context.getLocation().getLocationInfo() instanceof Long) || NULL_LOCATION.equals(context.getLocation())) {
6151d86c
PT
246 return null;
247 }
248
249 CustomXmlEvent event = null;
250 try {
a7418109
MK
251 // Below +1 for the <
252 if (fFile.getFilePointer() != (Long) context.getLocation().getLocationInfo() + 1) {
253 fFile.seek((Long) context.getLocation().getLocationInfo() + 1);
6151d86c
PT
254 }
255 final StringBuffer elementBuffer = new StringBuffer("<"); //$NON-NLS-1$
256 readElement(elementBuffer, fFile);
257 final Element element = parseElementBuffer(elementBuffer);
258
259 event = extractEvent(element, fRecordInputElement);
260 ((StringBuffer) event.getContent().getValue()).append(elementBuffer);
261
6151d86c 262 long rawPos = fFile.getFilePointer();
cd43d683
PT
263 String line = fFile.getNextLine();
264 while (line != null) {
a7418109 265 final int idx = indexOfElement(fRecordInputElement.getElementName(), line, 0);
6151d86c
PT
266 if (idx != -1) {
267 context.setLocation(new TmfLongLocation(rawPos + idx));
268 return event;
269 }
270 rawPos = fFile.getFilePointer();
cd43d683 271 line = fFile.getNextLine();
6151d86c
PT
272 }
273 } catch (final IOException e) {
47aafe74 274 Activator.logError("Error parsing event. File: " + getPath(), e); //$NON-NLS-1$
6151d86c
PT
275
276 }
277 context.setLocation(NULL_LOCATION);
278 return event;
279 }
280
281 private Element parseElementBuffer(final StringBuffer elementBuffer) {
282 try {
283 final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
284 final DocumentBuilder db = dbf.newDocumentBuilder();
285
286 // The following allows xml parsing without access to the dtd
eb8ea213 287 final EntityResolver resolver = new EntityResolver() {
6151d86c 288 @Override
eb8ea213 289 public InputSource resolveEntity(final String publicId, final String systemId) {
6151d86c
PT
290 final String empty = ""; //$NON-NLS-1$
291 final ByteArrayInputStream bais = new ByteArrayInputStream(empty.getBytes());
292 return new InputSource(bais);
293 }
294 };
295 db.setEntityResolver(resolver);
296
297 // The following catches xml parsing exceptions
eb8ea213 298 db.setErrorHandler(new ErrorHandler() {
6151d86c 299 @Override
a7418109
MK
300 public void error(final SAXParseException saxparseexception) throws SAXException {
301 }
eb8ea213 302
6151d86c 303 @Override
a7418109
MK
304 public void warning(final SAXParseException saxparseexception) throws SAXException {
305 }
eb8ea213 306
6151d86c
PT
307 @Override
308 public void fatalError(final SAXParseException saxparseexception) throws SAXException {
309 throw saxparseexception;
eb8ea213
MK
310 }
311 });
6151d86c
PT
312
313 final Document doc = db.parse(new ByteArrayInputStream(elementBuffer.toString().getBytes()));
314 return doc.getDocumentElement();
315 } catch (final ParserConfigurationException e) {
47aafe74 316 Activator.logError("Error parsing element buffer. File:" + getPath(), e); //$NON-NLS-1$
6151d86c 317 } catch (final SAXException e) {
47aafe74 318 Activator.logError("Error parsing element buffer. File:" + getPath(), e); //$NON-NLS-1$
6151d86c 319 } catch (final IOException e) {
47aafe74 320 Activator.logError("Error parsing element buffer. File: " + getPath(), e); //$NON-NLS-1$
6151d86c
PT
321 }
322 return null;
323 }
324
9fa9acbb
PT
325 private static int indexOfElement(String elementName, String line, int fromIndex) {
326 final String recordElementStart = '<' + elementName;
327 int index = line.indexOf(recordElementStart, fromIndex);
328 if (index == -1) {
329 return index;
330 }
331 int nextCharIndex = index + recordElementStart.length();
332 if (nextCharIndex < line.length()) {
333 char c = line.charAt(nextCharIndex);
334 // Check that the match is not just a substring of another element
335 if (Character.isLetterOrDigit(c)) {
336 return indexOfElement(elementName, line, nextCharIndex);
337 }
338 }
339 return index;
340 }
341
6151d86c
PT
342 private void readElement(final StringBuffer buffer, final RandomAccessFile raFile) {
343 try {
344 int numRead = 0;
345 boolean startTagClosed = false;
346 int i;
347 while ((i = raFile.read()) != -1) {
348 numRead++;
eb8ea213 349 final char c = (char) i;
6151d86c
PT
350 buffer.append(c);
351 if (c == '"') {
352 readQuote(buffer, raFile, '"');
353 } else if (c == '\'') {
354 readQuote(buffer, raFile, '\'');
355 } else if (c == '<') {
356 readElement(buffer, raFile);
357 } else if (c == '/' && numRead == 1) {
358 break; // found "</"
359 } else if (c == '-' && numRead == 3 && buffer.substring(buffer.length() - 3, buffer.length() - 1).equals("!-")) { //$NON-NLS-1$
360 readComment(buffer, raFile); // found "<!--"
361 } else if (i == '>') {
362 if (buffer.charAt(buffer.length() - 2) == '/') {
363 break; // found "/>"
364 } else if (startTagClosed) {
365 break; // found "<...>...</...>"
366 }
367 else {
368 startTagClosed = true; // found "<...>"
369 }
370 }
371 }
372 return;
373 } catch (final IOException e) {
374 return;
375 }
376 }
377
378 private static void readQuote(final StringBuffer buffer,
379 final RandomAccessFile raFile, final char eq) {
380 try {
381 int i;
382 while ((i = raFile.read()) != -1) {
eb8ea213 383 final char c = (char) i;
6151d86c
PT
384 buffer.append(c);
385 if (c == eq)
386 {
387 break; // found matching end-quote
388 }
389 }
390 return;
391 } catch (final IOException e) {
392 return;
393 }
394 }
395
396 private static void readComment(final StringBuffer buffer,
397 final RandomAccessFile raFile) {
398 try {
399 int numRead = 0;
400 int i;
401 while ((i = raFile.read()) != -1) {
402 numRead++;
eb8ea213 403 final char c = (char) i;
6151d86c
PT
404 buffer.append(c);
405 if (c == '>' && numRead >= 2 && buffer.substring(buffer.length() - 3, buffer.length() - 1).equals("--")) //$NON-NLS-1$
406 {
407 break; // found "-->"
408 }
409 }
410 return;
411 } catch (final IOException e) {
412 return;
413 }
414 }
415
a0a88f65
AM
416 /**
417 * Parse an XML element.
418 *
419 * @param parentElement
420 * The parent element
421 * @param buffer
422 * The contents to parse
423 * @return The parsed content
424 */
6151d86c
PT
425 public static StringBuffer parseElement(final Element parentElement, final StringBuffer buffer) {
426 final NodeList nodeList = parentElement.getChildNodes();
427 String separator = null;
428 for (int i = 0; i < nodeList.getLength(); i++) {
429 final Node node = nodeList.item(i);
430 if (node.getNodeType() == Node.ELEMENT_NODE) {
431 if (separator == null) {
432 separator = " | "; //$NON-NLS-1$
433 } else {
434 buffer.append(separator);
435 }
436 final Element element = (Element) node;
437 if (!element.hasChildNodes()) {
438 buffer.append(element.getNodeName());
439 } else if (element.getChildNodes().getLength() == 1 && element.getFirstChild().getNodeType() == Node.TEXT_NODE) {
440 buffer.append(element.getNodeName() + ":" + element.getFirstChild().getNodeValue().trim()); //$NON-NLS-1$
441 } else {
442 buffer.append(element.getNodeName());
443 buffer.append(" [ "); //$NON-NLS-1$
444 parseElement(element, buffer);
445 buffer.append(" ]"); //$NON-NLS-1$
446 }
447 } else if (node.getNodeType() == Node.TEXT_NODE) {
448 if (node.getNodeValue().trim().length() != 0) {
449 buffer.append(node.getNodeValue().trim());
450 }
451 }
452 }
453 return buffer;
454 }
455
a0a88f65
AM
456 /**
457 * Get an input element if it is a valid record input. If not, we will look
458 * into its children for valid inputs.
459 *
460 * @param inputElement
461 * The main element to check for.
462 * @return The record element
463 */
a7418109
MK
464 public CustomXmlInputElement getRecordInputElement(final CustomXmlInputElement inputElement) {
465 if (inputElement.isLogEntry()) {
6151d86c 466 return inputElement;
a7418109
MK
467 } else if (inputElement.getChildElements() != null) {
468 for (final CustomXmlInputElement childInputElement : inputElement.getChildElements()) {
469 final CustomXmlInputElement recordInputElement = getRecordInputElement(childInputElement);
6151d86c
PT
470 if (recordInputElement != null) {
471 return recordInputElement;
472 }
473 }
474 }
475 return null;
476 }
477
a0a88f65
AM
478 /**
479 * Extract a trace event from an XML element.
480 *
481 * @param element
482 * The element
483 * @param inputElement
484 * The input element
485 * @return The extracted event
486 */
a7418109 487 public CustomXmlEvent extractEvent(final Element element, final CustomXmlInputElement inputElement) {
e1de2fd4 488 final CustomXmlEvent event = new CustomXmlEvent(fDefinition, this, TmfTimestamp.ZERO, fEventType);
6151d86c
PT
489 event.setContent(new CustomEventContent(event, new StringBuffer()));
490 parseElement(element, event, inputElement);
491 return event;
492 }
493
a7418109
MK
494 private void parseElement(final Element element, final CustomXmlEvent event, final CustomXmlInputElement inputElement) {
495 if (inputElement.getInputName() != null && !inputElement.getInputName().equals(CustomXmlTraceDefinition.TAG_IGNORE)) {
496 event.parseInput(parseElement(element, new StringBuffer()).toString(), inputElement.getInputName(), inputElement.getInputAction(), inputElement.getInputFormat());
6151d86c 497 }
a7418109
MK
498 if (inputElement.getAttributes() != null) {
499 for (final CustomXmlInputAttribute attribute : inputElement.getAttributes()) {
500 event.parseInput(element.getAttribute(attribute.getAttributeName()), attribute.getInputName(), attribute.getInputAction(), attribute.getInputFormat());
6151d86c
PT
501 }
502 }
503 final NodeList childNodes = element.getChildNodes();
a7418109 504 if (inputElement.getChildElements() != null) {
6151d86c
PT
505 for (int i = 0; i < childNodes.getLength(); i++) {
506 final Node node = childNodes.item(i);
507 if (node instanceof Element) {
a7418109
MK
508 for (final CustomXmlInputElement child : inputElement.getChildElements()) {
509 if (node.getNodeName().equals(child.getElementName())) {
6151d86c
PT
510 parseElement((Element) node, event, child);
511 break;
512 }
513 }
514 }
515 }
516 }
517 return;
518 }
519
a0a88f65
AM
520 /**
521 * Retrieve the trace definition.
522 *
523 * @return The trace definition
524 */
6151d86c
PT
525 public CustomTraceDefinition getDefinition() {
526 return fDefinition;
527 }
528
cd43d683
PT
529 /**
530 * {@inheritDoc}
531 * <p>
532 * The default implementation sets the confidence to 100 if any of the first
533 * 100 lines of the file contains a valid record input element, and 0
534 * otherwise.
535 */
6151d86c 536 @Override
a94410d9 537 public IStatus validate(IProject project, String path) {
cd43d683
PT
538 File file = new File(path);
539 if (!file.exists() || !file.isFile() || !file.canRead()) {
540 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, Messages.CustomTrace_FileNotFound + ": " + path); //$NON-NLS-1$
541 }
542 try (BufferedRandomAccessFile rafile = new BufferedRandomAccessFile(path, "r")) { //$NON-NLS-1$
543 int lineCount = 0;
cd43d683
PT
544 long rawPos = 0;
545 String line = rafile.getNextLine();
546 while ((line != null) && (lineCount++ < MAX_LINES)) {
a7418109 547 final int idx = indexOfElement(fRecordInputElement.getElementName(), line, 0);
cd43d683
PT
548 if (idx != -1) {
549 rafile.seek(rawPos + idx + 1); // +1 is for the <
550 final StringBuffer elementBuffer = new StringBuffer("<"); //$NON-NLS-1$
551 readElement(elementBuffer, rafile);
552 final Element element = parseElementBuffer(elementBuffer);
553 if (element != null) {
554 rafile.close();
555 return new TraceValidationStatus(CONFIDENCE, Activator.PLUGIN_ID);
eb8ea213 556 }
cd43d683
PT
557 }
558 rawPos = rafile.getFilePointer();
559 line = rafile.getNextLine();
eb8ea213 560 }
cd43d683
PT
561 } catch (IOException e) {
562 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "IOException validating file: " + path, e); //$NON-NLS-1$
a94410d9 563 }
cd43d683 564 return new TraceValidationStatus(0, Activator.PLUGIN_ID);
6151d86c 565 }
032ecd45
MAL
566
567 private static int fCheckpointSize = -1;
568
569 @Override
570 public synchronized int getCheckpointSize() {
571 if (fCheckpointSize == -1) {
572 TmfCheckpoint c = new TmfCheckpoint(TmfTimestamp.ZERO, new TmfLongLocation(0L), 0);
573 ByteBuffer b = ByteBuffer.allocate(ITmfCheckpoint.MAX_SERIALIZE_SIZE);
574 b.clear();
575 c.serialize(b);
576 fCheckpointSize = b.position();
577 }
578
579 return fCheckpointSize;
580 }
581
582 @Override
583 public ITmfLocation restoreLocation(ByteBuffer bufferIn) {
584 return new TmfLongLocation(bufferIn);
585 }
586
587 @Override
588 protected ITmfTraceIndexer createIndexer(int interval) {
589 return new TmfBTreeTraceIndexer(this, interval);
590 }
6151d86c 591}
This page took 0.085481 seconds and 5 git commands to generate.