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