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