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