BTree index on disk
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / internal / tmf / ui / parsers / custom / CustomXmlTrace.java
CommitLineData
6151d86c 1/*******************************************************************************
c8422608 2 * Copyright (c) 2010, 2013 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
13package org.eclipse.linuxtools.internal.tmf.ui.parsers.custom;
14
15import java.io.ByteArrayInputStream;
eb8ea213
MK
16import java.io.File;
17import java.io.FileInputStream;
18import java.io.FileNotFoundException;
6151d86c
PT
19import java.io.IOException;
20import java.io.RandomAccessFile;
032ecd45 21import java.nio.ByteBuffer;
6151d86c
PT
22
23import javax.xml.parsers.DocumentBuilder;
24import javax.xml.parsers.DocumentBuilderFactory;
25import javax.xml.parsers.ParserConfigurationException;
eb8ea213 26
6151d86c
PT
27import org.eclipse.core.resources.IProject;
28import org.eclipse.core.resources.IResource;
a3db8436
AM
29import org.eclipse.core.runtime.IStatus;
30import org.eclipse.core.runtime.Status;
6151d86c
PT
31import org.eclipse.linuxtools.internal.tmf.ui.Activator;
32import org.eclipse.linuxtools.internal.tmf.ui.parsers.custom.CustomXmlTraceDefinition.InputAttribute;
33import org.eclipse.linuxtools.internal.tmf.ui.parsers.custom.CustomXmlTraceDefinition.InputElement;
34import org.eclipse.linuxtools.tmf.core.event.ITmfEvent;
6151d86c
PT
35import org.eclipse.linuxtools.tmf.core.exceptions.TmfTraceException;
36import org.eclipse.linuxtools.tmf.core.io.BufferedRandomAccessFile;
3bd46eef 37import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestamp;
6151d86c
PT
38import org.eclipse.linuxtools.tmf.core.trace.ITmfContext;
39import org.eclipse.linuxtools.tmf.core.trace.ITmfEventParser;
6151d86c 40import org.eclipse.linuxtools.tmf.core.trace.TmfContext;
6151d86c 41import org.eclipse.linuxtools.tmf.core.trace.TmfTrace;
032ecd45 42import org.eclipse.linuxtools.tmf.core.trace.indexer.ITmfPersistentlyIndexable;
a3db8436 43import org.eclipse.linuxtools.tmf.core.trace.indexer.ITmfTraceIndexer;
032ecd45
MAL
44import org.eclipse.linuxtools.tmf.core.trace.indexer.TmfBTreeTraceIndexer;
45import org.eclipse.linuxtools.tmf.core.trace.indexer.checkpoint.ITmfCheckpoint;
46import org.eclipse.linuxtools.tmf.core.trace.indexer.checkpoint.TmfCheckpoint;
a3db8436
AM
47import org.eclipse.linuxtools.tmf.core.trace.location.ITmfLocation;
48import org.eclipse.linuxtools.tmf.core.trace.location.TmfLongLocation;
6151d86c
PT
49import org.w3c.dom.Document;
50import org.w3c.dom.Element;
51import org.w3c.dom.Node;
52import org.w3c.dom.NodeList;
53import org.xml.sax.EntityResolver;
54import org.xml.sax.ErrorHandler;
55import org.xml.sax.InputSource;
56import org.xml.sax.SAXException;
57import org.xml.sax.SAXParseException;
58
a0a88f65
AM
59/**
60 * Trace object for custom XML trace parsers.
61 *
62 * @author Patrick Tassé
63 */
032ecd45 64public class CustomXmlTrace extends TmfTrace implements ITmfEventParser, ITmfPersistentlyIndexable {
6151d86c
PT
65
66 private static final TmfLongLocation NULL_LOCATION = new TmfLongLocation((Long) null);
67 private static final int DEFAULT_CACHE_SIZE = 100;
68
69 private final CustomXmlTraceDefinition fDefinition;
70 private final CustomXmlEventType fEventType;
71 private final InputElement fRecordInputElement;
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 }
149 String line;
150 final String recordElementStart = "<" + fRecordInputElement.elementName; //$NON-NLS-1$
151 long rawPos = fFile.getFilePointer();
152
153 while ((line = fFile.getNextLine()) != null) {
154 final int idx = line.indexOf(recordElementStart);
155 if (idx != -1) {
156 context.setLocation(new TmfLongLocation(rawPos + idx));
157 return context;
158 }
159 rawPos = fFile.getFilePointer();
160 }
161 return context;
162 } catch (final IOException e) {
163 Activator.getDefault().logError("Error seeking event. File: " + getPath(), e); //$NON-NLS-1$
164 return context;
165 }
166
167 }
168
169 @Override
170 public synchronized TmfContext seekEvent(final double ratio) {
171 if (fFile == null) {
172 return new CustomTxtTraceContext(NULL_LOCATION, ITmfContext.UNKNOWN_RANK);
173 }
174 try {
91f6e587 175 long pos = Math.round(ratio * fFile.length());
6151d86c
PT
176 while (pos > 0) {
177 fFile.seek(pos - 1);
178 if (fFile.read() == '\n') {
179 break;
180 }
181 pos--;
182 }
183 final ITmfLocation location = new TmfLongLocation(pos);
184 final TmfContext context = seekEvent(location);
185 context.setRank(ITmfContext.UNKNOWN_RANK);
186 return context;
187 } catch (final IOException e) {
188 Activator.getDefault().logError("Error seeking event. File: " + getPath(), e); //$NON-NLS-1$
189 return new CustomXmlTraceContext(NULL_LOCATION, ITmfContext.UNKNOWN_RANK);
190 }
191 }
192
193 @Override
194 public synchronized double getLocationRatio(final ITmfLocation location) {
195 if (fFile == null) {
196 return 0;
197 }
198 try {
199 if (location.getLocationInfo() instanceof Long) {
200 return (double) ((Long) location.getLocationInfo()) / fFile.length();
201 }
202 } catch (final IOException e) {
203 Activator.getDefault().logError("Error getting location ration. File: " + getPath(), e); //$NON-NLS-1$
204 }
205 return 0;
206 }
207
208 @Override
209 public ITmfLocation getCurrentLocation() {
210 // TODO Auto-generated method stub
211 return null;
212 }
213
214 @Override
215 public synchronized CustomXmlEvent parseEvent(final ITmfContext tmfContext) {
216 ITmfContext context = seekEvent(tmfContext.getLocation());
217 return parse(context);
218 }
219
220 @Override
221 public synchronized CustomXmlEvent getNext(final ITmfContext context) {
4c9f2944 222 final ITmfContext savedContext = new TmfContext(context.getLocation(), context.getRank());
6151d86c
PT
223 final CustomXmlEvent event = parse(context);
224 if (event != null) {
225 updateAttributes(savedContext, event.getTimestamp());
226 context.increaseRank();
227 }
228 return event;
229 }
230
231 private synchronized CustomXmlEvent parse(final ITmfContext tmfContext) {
232 if (fFile == null) {
233 return null;
234 }
235 if (!(tmfContext instanceof CustomXmlTraceContext)) {
236 return null;
237 }
238
239 final CustomXmlTraceContext context = (CustomXmlTraceContext) tmfContext;
9cbe7899 240 if (context.getLocation() == null || !(context.getLocation().getLocationInfo() instanceof Long) || NULL_LOCATION.equals(context.getLocation())) {
6151d86c
PT
241 return null;
242 }
243
244 CustomXmlEvent event = null;
245 try {
eb8ea213 246 if (fFile.getFilePointer() != (Long) context.getLocation().getLocationInfo() + 1)
6151d86c 247 {
eb8ea213 248 fFile.seek((Long) context.getLocation().getLocationInfo() + 1); // +1 is for the <
6151d86c
PT
249 }
250 final StringBuffer elementBuffer = new StringBuffer("<"); //$NON-NLS-1$
251 readElement(elementBuffer, fFile);
252 final Element element = parseElementBuffer(elementBuffer);
253
254 event = extractEvent(element, fRecordInputElement);
255 ((StringBuffer) event.getContent().getValue()).append(elementBuffer);
256
257 String line;
258 final String recordElementStart = "<" + fRecordInputElement.elementName; //$NON-NLS-1$
259 long rawPos = fFile.getFilePointer();
260
261 while ((line = fFile.getNextLine()) != null) {
262 final int idx = line.indexOf(recordElementStart);
263 if (idx != -1) {
264 context.setLocation(new TmfLongLocation(rawPos + idx));
265 return event;
266 }
267 rawPos = fFile.getFilePointer();
268 }
269 } catch (final IOException e) {
270 Activator.getDefault().logError("Error parsing event. File: " + getPath(), e); //$NON-NLS-1$
271
272 }
273 context.setLocation(NULL_LOCATION);
274 return event;
275 }
276
277 private Element parseElementBuffer(final StringBuffer elementBuffer) {
278 try {
279 final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
280 final DocumentBuilder db = dbf.newDocumentBuilder();
281
282 // The following allows xml parsing without access to the dtd
eb8ea213 283 final EntityResolver resolver = new EntityResolver() {
6151d86c 284 @Override
eb8ea213 285 public InputSource resolveEntity(final String publicId, final String systemId) {
6151d86c
PT
286 final String empty = ""; //$NON-NLS-1$
287 final ByteArrayInputStream bais = new ByteArrayInputStream(empty.getBytes());
288 return new InputSource(bais);
289 }
290 };
291 db.setEntityResolver(resolver);
292
293 // The following catches xml parsing exceptions
eb8ea213 294 db.setErrorHandler(new ErrorHandler() {
6151d86c
PT
295 @Override
296 public void error(final SAXParseException saxparseexception) throws SAXException {}
eb8ea213 297
6151d86c
PT
298 @Override
299 public void warning(final SAXParseException saxparseexception) throws SAXException {}
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) {
310 Activator.getDefault().logError("Error parsing element buffer. File:" + getPath(), e); //$NON-NLS-1$
311 } catch (final SAXException e) {
312 Activator.getDefault().logError("Error parsing element buffer. File:" + getPath(), e); //$NON-NLS-1$
313 } catch (final IOException e) {
314 Activator.getDefault().logError("Error parsing element buffer. File: " + getPath(), e); //$NON-NLS-1$
315 }
316 return null;
317 }
318
319 private void readElement(final StringBuffer buffer, final RandomAccessFile raFile) {
320 try {
321 int numRead = 0;
322 boolean startTagClosed = false;
323 int i;
324 while ((i = raFile.read()) != -1) {
325 numRead++;
eb8ea213 326 final char c = (char) i;
6151d86c
PT
327 buffer.append(c);
328 if (c == '"') {
329 readQuote(buffer, raFile, '"');
330 } else if (c == '\'') {
331 readQuote(buffer, raFile, '\'');
332 } else if (c == '<') {
333 readElement(buffer, raFile);
334 } else if (c == '/' && numRead == 1) {
335 break; // found "</"
336 } else if (c == '-' && numRead == 3 && buffer.substring(buffer.length() - 3, buffer.length() - 1).equals("!-")) { //$NON-NLS-1$
337 readComment(buffer, raFile); // found "<!--"
338 } else if (i == '>') {
339 if (buffer.charAt(buffer.length() - 2) == '/') {
340 break; // found "/>"
341 } else if (startTagClosed) {
342 break; // found "<...>...</...>"
343 }
344 else {
345 startTagClosed = true; // found "<...>"
346 }
347 }
348 }
349 return;
350 } catch (final IOException e) {
351 return;
352 }
353 }
354
355 private static void readQuote(final StringBuffer buffer,
356 final RandomAccessFile raFile, final char eq) {
357 try {
358 int i;
359 while ((i = raFile.read()) != -1) {
eb8ea213 360 final char c = (char) i;
6151d86c
PT
361 buffer.append(c);
362 if (c == eq)
363 {
364 break; // found matching end-quote
365 }
366 }
367 return;
368 } catch (final IOException e) {
369 return;
370 }
371 }
372
373 private static void readComment(final StringBuffer buffer,
374 final RandomAccessFile raFile) {
375 try {
376 int numRead = 0;
377 int i;
378 while ((i = raFile.read()) != -1) {
379 numRead++;
eb8ea213 380 final char c = (char) i;
6151d86c
PT
381 buffer.append(c);
382 if (c == '>' && numRead >= 2 && buffer.substring(buffer.length() - 3, buffer.length() - 1).equals("--")) //$NON-NLS-1$
383 {
384 break; // found "-->"
385 }
386 }
387 return;
388 } catch (final IOException e) {
389 return;
390 }
391 }
392
a0a88f65
AM
393 /**
394 * Parse an XML element.
395 *
396 * @param parentElement
397 * The parent element
398 * @param buffer
399 * The contents to parse
400 * @return The parsed content
401 */
6151d86c
PT
402 public static StringBuffer parseElement(final Element parentElement, final StringBuffer buffer) {
403 final NodeList nodeList = parentElement.getChildNodes();
404 String separator = null;
405 for (int i = 0; i < nodeList.getLength(); i++) {
406 final Node node = nodeList.item(i);
407 if (node.getNodeType() == Node.ELEMENT_NODE) {
408 if (separator == null) {
409 separator = " | "; //$NON-NLS-1$
410 } else {
411 buffer.append(separator);
412 }
413 final Element element = (Element) node;
414 if (!element.hasChildNodes()) {
415 buffer.append(element.getNodeName());
416 } else if (element.getChildNodes().getLength() == 1 && element.getFirstChild().getNodeType() == Node.TEXT_NODE) {
417 buffer.append(element.getNodeName() + ":" + element.getFirstChild().getNodeValue().trim()); //$NON-NLS-1$
418 } else {
419 buffer.append(element.getNodeName());
420 buffer.append(" [ "); //$NON-NLS-1$
421 parseElement(element, buffer);
422 buffer.append(" ]"); //$NON-NLS-1$
423 }
424 } else if (node.getNodeType() == Node.TEXT_NODE) {
425 if (node.getNodeValue().trim().length() != 0) {
426 buffer.append(node.getNodeValue().trim());
427 }
428 }
429 }
430 return buffer;
431 }
432
a0a88f65
AM
433 /**
434 * Get an input element if it is a valid record input. If not, we will look
435 * into its children for valid inputs.
436 *
437 * @param inputElement
438 * The main element to check for.
439 * @return The record element
440 */
6151d86c
PT
441 public InputElement getRecordInputElement(final InputElement inputElement) {
442 if (inputElement.logEntry) {
443 return inputElement;
444 } else if (inputElement.childElements != null) {
445 for (final InputElement childInputElement : inputElement.childElements) {
446 final InputElement recordInputElement = getRecordInputElement(childInputElement);
447 if (recordInputElement != null) {
448 return recordInputElement;
449 }
450 }
451 }
452 return null;
453 }
454
a0a88f65
AM
455 /**
456 * Extract a trace event from an XML element.
457 *
458 * @param element
459 * The element
460 * @param inputElement
461 * The input element
462 * @return The extracted event
463 */
6151d86c 464 public CustomXmlEvent extractEvent(final Element element, final InputElement inputElement) {
eb8ea213 465 final CustomXmlEvent event = new CustomXmlEvent(fDefinition, this, TmfTimestamp.ZERO, "", fEventType, ""); //$NON-NLS-1$ //$NON-NLS-2$
6151d86c
PT
466 event.setContent(new CustomEventContent(event, new StringBuffer()));
467 parseElement(element, event, inputElement);
468 return event;
469 }
470
471 private void parseElement(final Element element, final CustomXmlEvent event, final InputElement inputElement) {
472 if (inputElement.inputName != null && !inputElement.inputName.equals(CustomXmlTraceDefinition.TAG_IGNORE)) {
473 event.parseInput(parseElement(element, new StringBuffer()).toString(), inputElement.inputName, inputElement.inputAction, inputElement.inputFormat);
474 }
475 if (inputElement.attributes != null) {
476 for (final InputAttribute attribute : inputElement.attributes) {
477 event.parseInput(element.getAttribute(attribute.attributeName), attribute.inputName, attribute.inputAction, attribute.inputFormat);
478 }
479 }
480 final NodeList childNodes = element.getChildNodes();
481 if (inputElement.childElements != null) {
482 for (int i = 0; i < childNodes.getLength(); i++) {
483 final Node node = childNodes.item(i);
484 if (node instanceof Element) {
485 for (final InputElement child : inputElement.childElements) {
486 if (node.getNodeName().equals(child.elementName)) {
487 parseElement((Element) node, event, child);
488 break;
489 }
490 }
491 }
492 }
493 }
494 return;
495 }
496
a0a88f65
AM
497 /**
498 * Retrieve the trace definition.
499 *
500 * @return The trace definition
501 */
6151d86c
PT
502 public CustomTraceDefinition getDefinition() {
503 return fDefinition;
504 }
505
6151d86c 506 @Override
a94410d9 507 public IStatus validate(IProject project, String path) {
eb8ea213
MK
508 File xmlFile = new File(path);
509 if (xmlFile.exists() && xmlFile.isFile() && xmlFile.canRead() && xmlFile.length() > 0) {
510 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
511 DocumentBuilder db;
512 try {
513 db = dbf.newDocumentBuilder();
514
515 // The following allows xml parsing without access to the dtd
516 EntityResolver resolver = new EntityResolver() {
517 @Override
518 public InputSource resolveEntity(String publicId, String systemId) {
519 return new InputSource(new ByteArrayInputStream(new byte[0]));
520 }
521 };
522 db.setEntityResolver(resolver);
523
524 // The following catches xml parsing exceptions
525 db.setErrorHandler(new ErrorHandler() {
526 @Override
527 public void error(SAXParseException saxparseexception) throws SAXException {
528 }
529
530 @Override
531 public void warning(SAXParseException saxparseexception) throws SAXException {
532 }
533
534 @Override
535 public void fatalError(SAXParseException saxparseexception) throws SAXException {
536 throw saxparseexception;
537 }
538 });
539 db.parse(new FileInputStream(xmlFile));
540 return Status.OK_STATUS;
541 } catch (ParserConfigurationException e) {
542 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e);
543 } catch (FileNotFoundException e) {
544 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e);
545 } catch (SAXException e) {
546 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e);
547 } catch (IOException e) {
548 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e);
549 }
a94410d9
MK
550 }
551 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, Messages.CustomTrace_FileNotFound + ": " + path); //$NON-NLS-1$
6151d86c 552 }
032ecd45
MAL
553
554 private static int fCheckpointSize = -1;
555
556 @Override
557 public synchronized int getCheckpointSize() {
558 if (fCheckpointSize == -1) {
559 TmfCheckpoint c = new TmfCheckpoint(TmfTimestamp.ZERO, new TmfLongLocation(0L), 0);
560 ByteBuffer b = ByteBuffer.allocate(ITmfCheckpoint.MAX_SERIALIZE_SIZE);
561 b.clear();
562 c.serialize(b);
563 fCheckpointSize = b.position();
564 }
565
566 return fCheckpointSize;
567 }
568
569 @Override
570 public ITmfLocation restoreLocation(ByteBuffer bufferIn) {
571 return new TmfLongLocation(bufferIn);
572 }
573
574 @Override
575 protected ITmfTraceIndexer createIndexer(int interval) {
576 return new TmfBTreeTraceIndexer(this, interval);
577 }
6151d86c 578}
This page took 0.057324 seconds and 5 git commands to generate.