Make Custom Parser trace type backwards compatible to Linux Tools
[deliverable/tracecompass.git] / org.eclipse.tracecompass.tmf.core / src / org / eclipse / tracecompass / tmf / core / parsers / custom / CustomTxtTraceDefinition.java
1 /*******************************************************************************
2 * Copyright (c) 2010, 2015 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 * Matthew Khouzam - Add support for default parsers
12 *******************************************************************************/
13
14 package org.eclipse.tracecompass.tmf.core.parsers.custom;
15
16 import java.io.File;
17 import java.io.FileWriter;
18 import java.io.IOException;
19 import java.io.StringWriter;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Comparator;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26 import java.util.TreeSet;
27 import java.util.regex.Pattern;
28 import java.util.regex.PatternSyntaxException;
29
30 import javax.xml.parsers.DocumentBuilder;
31 import javax.xml.parsers.DocumentBuilderFactory;
32 import javax.xml.parsers.ParserConfigurationException;
33 import javax.xml.transform.OutputKeys;
34 import javax.xml.transform.Transformer;
35 import javax.xml.transform.TransformerConfigurationException;
36 import javax.xml.transform.TransformerException;
37 import javax.xml.transform.TransformerFactory;
38 import javax.xml.transform.TransformerFactoryConfigurationError;
39 import javax.xml.transform.dom.DOMSource;
40 import javax.xml.transform.stream.StreamResult;
41
42 import org.eclipse.core.runtime.Platform;
43 import org.eclipse.tracecompass.internal.tmf.core.Activator;
44 import org.eclipse.tracecompass.tmf.core.project.model.TmfTraceType;
45 import org.w3c.dom.Document;
46 import org.w3c.dom.Element;
47 import org.w3c.dom.Node;
48 import org.w3c.dom.NodeList;
49 import org.xml.sax.SAXException;
50
51 /**
52 * Trace definition for custom text traces.
53 *
54 * @author Patrick Tassé
55 * @since 3.0
56 */
57 public class CustomTxtTraceDefinition extends CustomTraceDefinition {
58
59 /** Input lines */
60 public List<InputLine> inputs;
61
62 /**
63 * Custom text label used internally and therefore should not be
64 * externalized
65 */
66 public static final String CUSTOM_TXT_CATEGORY = "Custom Text"; //$NON-NLS-1$
67
68
69 /** File name of the default definition file */
70 protected static final String CUSTOM_TXT_TRACE_DEFINITIONS_DEFAULT_FILE_NAME = "custom_txt_default_parsers.xml"; //$NON-NLS-1$
71 /** File name of the definition file */
72 protected static final String CUSTOM_TXT_TRACE_DEFINITIONS_FILE_NAME = "custom_txt_parsers.xml"; //$NON-NLS-1$
73
74 /** Path of the definition file */
75 protected static final String CUSTOM_TXT_TRACE_DEFINITIONS_DEFAULT_PATH_NAME =
76 Platform.getInstallLocation().getURL().getPath() + "templates/org.eclipse.linuxtools.tmf.core/" + //$NON-NLS-1$
77 CUSTOM_TXT_TRACE_DEFINITIONS_DEFAULT_FILE_NAME;
78 /** Path of the definition file */
79 protected static final String CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME =
80 Activator.getDefault().getStateLocation().addTrailingSeparator().append(CUSTOM_TXT_TRACE_DEFINITIONS_FILE_NAME).toString();
81
82 /**
83 * Legacy path to the XML definitions file (in the UI plug-in of linuxtools) TODO Remove
84 * once we feel the transition phase is over.
85 */
86 private static final String CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME_LEGACY_UI =
87 Activator.getDefault().getStateLocation().removeLastSegments(1).addTrailingSeparator()
88 .append("org.eclipse.linuxtools.tmf.ui") //$NON-NLS-1$
89 .append(CUSTOM_TXT_TRACE_DEFINITIONS_FILE_NAME).toString();
90
91 /**
92 * Legacy path to the XML definitions file (in the core plug-in of linuxtools) TODO Remove
93 * once we feel the transition phase is over.
94 */
95 private static final String CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME_LEGACY_CORE =
96 Activator.getDefault().getStateLocation().removeLastSegments(1).addTrailingSeparator()
97 .append("org.eclipse.linuxtools.tmf.core") //$NON-NLS-1$
98 .append(CUSTOM_TXT_TRACE_DEFINITIONS_FILE_NAME).toString();
99
100 private static final String CUSTOM_TXT_TRACE_DEFINITION_ROOT_ELEMENT = Messages.CustomTxtTraceDefinition_definitionRootElement;
101 private static final String DEFINITION_ELEMENT = Messages.CustomTxtTraceDefinition_definition;
102 private static final String CATEGORY_ATTRIBUTE = Messages.CustomTxtTraceDefinition_category;
103 private static final String NAME_ATTRIBUTE = Messages.CustomTxtTraceDefinition_name;
104 private static final String TIME_STAMP_OUTPUT_FORMAT_ELEMENT = Messages.CustomTxtTraceDefinition_timestampOutputFormat;
105 private static final String INPUT_LINE_ELEMENT = Messages.CustomTxtTraceDefinition_inputLine;
106 private static final String CARDINALITY_ELEMENT = Messages.CustomTxtTraceDefinition_cardinality;
107 private static final String MIN_ATTRIBUTE = Messages.CustomTxtTraceDefinition_min;
108 private static final String MAX_ATTRIBUTE = Messages.CustomTxtTraceDefinition_max;
109 private static final String REGEX_ELEMENT = Messages.CustomTxtTraceDefinition_regEx;
110 private static final String INPUT_DATA_ELEMENT = Messages.CustomTxtTraceDefinition_inputData;
111 private static final String ACTION_ATTRIBUTE = Messages.CustomTxtTraceDefinition_action;
112 private static final String FORMAT_ATTRIBUTE = Messages.CustomTxtTraceDefinition_format;
113 private static final String OUTPUT_COLUMN_ELEMENT = Messages.CustomTxtTraceDefinition_outputColumn;
114
115 /**
116 * Default constructor.
117 */
118 public CustomTxtTraceDefinition() {
119 this(CUSTOM_TXT_CATEGORY, "", new ArrayList<InputLine>(0), new ArrayList<OutputColumn>(0), ""); //$NON-NLS-1$ //$NON-NLS-2$
120 }
121
122 /**
123 * Full constructor.
124 *
125 * @param traceType
126 * Name of the trace type
127 * @param inputs
128 * List of inputs
129 * @param outputs
130 * List of output columns
131 * @param timeStampOutputFormat
132 * The timestamp format to use
133 * @deprecated Use {@link #CustomTxtTraceDefinition(String, String, List, List, String)}
134 */
135 @Deprecated
136 public CustomTxtTraceDefinition(String traceType, List<InputLine> inputs,
137 List<OutputColumn> outputs, String timeStampOutputFormat) {
138 this.definitionName = traceType;
139 this.inputs = inputs;
140 this.outputs = outputs;
141 this.timeStampOutputFormat = timeStampOutputFormat;
142 }
143
144 /**
145 * Full constructor.
146 *
147 * @param category
148 * Category of the trace type
149 * @param traceType
150 * Name of the trace type
151 * @param inputs
152 * List of inputs
153 * @param outputs
154 * List of output columns
155 * @param timeStampOutputFormat
156 * The timestamp format to use
157 * @since 3.2
158 */
159 public CustomTxtTraceDefinition(String category, String traceType, List<InputLine> inputs,
160 List<OutputColumn> outputs, String timeStampOutputFormat) {
161 this.categoryName = category;
162 this.definitionName = traceType;
163 this.inputs = inputs;
164 this.outputs = outputs;
165 this.timeStampOutputFormat = timeStampOutputFormat;
166 }
167
168 /**
169 * Wrapper to store a line of the log file
170 */
171 public static class InputLine {
172
173 /** Data columns of this line */
174 public List<InputData> columns;
175
176 /** Cardinality of this line (see {@link Cardinality}) */
177 public Cardinality cardinality;
178
179 /** Parent line */
180 public InputLine parentInput;
181
182 /** Level of this line */
183 public int level;
184
185 /** Next input line in the file */
186 public InputLine nextInput;
187
188 /** Children of this line (if one "event" spans many lines) */
189 public List<InputLine> childrenInputs;
190
191 private String regex;
192 private Pattern pattern;
193
194 /**
195 * Default (empty) constructor.
196 */
197 public InputLine() {
198 }
199
200 /**
201 * Constructor.
202 *
203 * @param cardinality
204 * Cardinality of this line.
205 * @param regex
206 * Regex
207 * @param columns
208 * Columns to use
209 */
210 public InputLine(Cardinality cardinality, String regex, List<InputData> columns) {
211 this.cardinality = cardinality;
212 this.regex = regex;
213 this.columns = columns;
214 }
215
216 /**
217 * Set the regex of this input line
218 *
219 * @param regex
220 * Regex to set
221 */
222 public void setRegex(String regex) {
223 this.regex = regex;
224 this.pattern = null;
225 }
226
227 /**
228 * Get the current regex
229 *
230 * @return The current regex
231 */
232 public String getRegex() {
233 return regex;
234 }
235
236 /**
237 * Get the Pattern object of this line's regex
238 *
239 * @return The Pattern
240 * @throws PatternSyntaxException
241 * If the regex does not parse correctly
242 */
243 public Pattern getPattern() throws PatternSyntaxException {
244 if (pattern == null) {
245 pattern = Pattern.compile(regex);
246 }
247 return pattern;
248 }
249
250 /**
251 * Add a child line to this line.
252 *
253 * @param input
254 * The child input line
255 */
256 public void addChild(InputLine input) {
257 if (childrenInputs == null) {
258 childrenInputs = new ArrayList<>(1);
259 } else if (childrenInputs.size() > 0) {
260 InputLine last = childrenInputs.get(childrenInputs.size() - 1);
261 last.nextInput = input;
262 }
263 childrenInputs.add(input);
264 input.parentInput = this;
265 input.level = this.level + 1;
266 }
267
268 /**
269 * Set the next input line.
270 *
271 * @param input
272 * The next input line
273 */
274 public void addNext(InputLine input) {
275 if (parentInput != null) {
276 int index = parentInput.childrenInputs.indexOf(this);
277 parentInput.childrenInputs.add(index + 1, input);
278 InputLine next = nextInput;
279 nextInput = input;
280 input.nextInput = next;
281 }
282 input.parentInput = this.parentInput;
283 input.level = this.level;
284 }
285
286 /**
287 * Move this line up in its parent's children.
288 */
289 public void moveUp() {
290 if (parentInput != null) {
291 int index = parentInput.childrenInputs.indexOf(this);
292 if (index > 0) {
293 parentInput.childrenInputs.add(index - 1, parentInput.childrenInputs.remove(index));
294 parentInput.childrenInputs.get(index).nextInput = nextInput;
295 nextInput = parentInput.childrenInputs.get(index);
296 }
297 }
298 }
299
300 /**
301 * Move this line down in its parent's children.
302 */
303 public void moveDown() {
304 if (parentInput != null) {
305 int index = parentInput.childrenInputs.indexOf(this);
306 if (index < parentInput.childrenInputs.size() - 1) {
307 parentInput.childrenInputs.add(index + 1, parentInput.childrenInputs.remove(index));
308 nextInput = parentInput.childrenInputs.get(index).nextInput;
309 parentInput.childrenInputs.get(index).nextInput = this;
310 }
311 }
312 }
313
314 /**
315 * Add a data column to this line
316 *
317 * @param column
318 * The column to add
319 */
320 public void addColumn(InputData column) {
321 if (columns == null) {
322 columns = new ArrayList<>(1);
323 }
324 columns.add(column);
325 }
326
327 /**
328 * Get the next input lines.
329 *
330 * @param countMap
331 * The map of line "sets".
332 * @return The next list of lines.
333 */
334 public List<InputLine> getNextInputs(Map<InputLine, Integer> countMap) {
335 List<InputLine> nextInputs = new ArrayList<>();
336 InputLine next = nextInput;
337 while (next != null) {
338 nextInputs.add(next);
339 if (next.cardinality.min > 0) {
340 return nextInputs;
341 }
342 next = next.nextInput;
343 }
344 if (parentInput != null && parentInput.level > 0) {
345 int parentCount = countMap.get(parentInput);
346 if (parentCount < parentInput.getMaxCount()) {
347 nextInputs.add(parentInput);
348 }
349 if (parentCount < parentInput.getMinCount()) {
350 return nextInputs;
351 }
352 nextInputs.addAll(parentInput.getNextInputs(countMap));
353 }
354 return nextInputs;
355 }
356
357 /**
358 * Get the minimum possible amount of entries.
359 *
360 * @return The minimum
361 */
362 public int getMinCount() {
363 return cardinality.min;
364 }
365
366 /**
367 * Get the maximum possible amount of entries.
368 *
369 * @return The maximum
370 */
371 public int getMaxCount() {
372 return cardinality.max;
373 }
374
375 @Override
376 public String toString() {
377 return regex + " " + cardinality; //$NON-NLS-1$
378 }
379 }
380
381 /**
382 * Data column for input lines.
383 */
384 public static class InputData {
385
386 /** Name of this column */
387 public String name;
388
389 /** Action id */
390 public int action;
391
392 /** Format */
393 public String format;
394
395 /**
396 * Default (empty) constructor
397 */
398 public InputData() {
399 }
400
401 /**
402 * Full constructor
403 *
404 * @param name
405 * Name
406 * @param action
407 * Action
408 * @param format
409 * Format
410 */
411 public InputData(String name, int action, String format) {
412 this.name = name;
413 this.action = action;
414 this.format = format;
415 }
416
417 /**
418 * Constructor with default format
419 *
420 * @param name
421 * Name
422 * @param action
423 * Action
424 */
425 public InputData(String name, int action) {
426 this.name = name;
427 this.action = action;
428 }
429 }
430
431 /**
432 * Input line cardinality
433 */
434 public static class Cardinality {
435
436 /** Representation of infinity */
437 public final static int INF = Integer.MAX_VALUE;
438
439 /** Preset for [1, 1] */
440 public final static Cardinality ONE = new Cardinality(1, 1);
441
442 /** Preset for [1, inf] */
443 public final static Cardinality ONE_OR_MORE = new Cardinality(1, INF);
444
445 /** Preset for [0, 1] */
446 public final static Cardinality ZERO_OR_ONE = new Cardinality(0, 1);
447
448 /** Preset for [0, inf] */
449 public final static Cardinality ZERO_OR_MORE = new Cardinality(0, INF);
450
451 private final int min;
452 private final int max;
453
454 /**
455 * Constructor.
456 *
457 * @param min
458 * Minimum
459 * @param max
460 * Maximum
461 */
462 public Cardinality(int min, int max) {
463 this.min = min;
464 this.max = max;
465 }
466
467 @Override
468 public String toString() {
469 return "(" + (min >= 0 ? min : "?") + ',' + (max == INF ? "\u221E" : (max >= 0 ? max : "?")) + ')'; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
470 }
471
472 @Override
473 public int hashCode() {
474 final int prime = 31;
475 int result = 1;
476 result = prime * result + max;
477 result = prime * result + min;
478 return result;
479 }
480
481 @Override
482 public boolean equals(Object obj) {
483 if (this == obj) {
484 return true;
485 }
486 if (obj == null) {
487 return false;
488 }
489 if (!(obj instanceof Cardinality)) {
490 return false;
491 }
492 Cardinality other = (Cardinality) obj;
493 return (this.min == other.min && this.max == other.max);
494 }
495 }
496
497 @Override
498 public void save() {
499 save(CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME);
500 }
501
502 @Override
503 public void save(String path) {
504 try {
505 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
506 DocumentBuilder db = dbf.newDocumentBuilder();
507
508 // The following allows xml parsing without access to the dtd
509 db.setEntityResolver(createEmptyEntityResolver());
510
511 // The following catches xml parsing exceptions
512 db.setErrorHandler(createErrorHandler());
513
514 Document doc = null;
515 File file = new File(path);
516 if (file.canRead()) {
517 doc = db.parse(file);
518 if (!doc.getDocumentElement().getNodeName().equals(CUSTOM_TXT_TRACE_DEFINITION_ROOT_ELEMENT)) {
519 return;
520 }
521 } else {
522 doc = db.newDocument();
523 Node node = doc.createElement(CUSTOM_TXT_TRACE_DEFINITION_ROOT_ELEMENT);
524 doc.appendChild(node);
525 }
526
527 Element root = doc.getDocumentElement();
528
529 Element oldDefinitionElement = findDefinitionElement(root, categoryName, definitionName);
530 if (oldDefinitionElement != null) {
531 root.removeChild(oldDefinitionElement);
532 }
533 Element definitionElement = doc.createElement(DEFINITION_ELEMENT);
534 root.appendChild(definitionElement);
535 definitionElement.setAttribute(CATEGORY_ATTRIBUTE, categoryName);
536 definitionElement.setAttribute(NAME_ATTRIBUTE, definitionName);
537
538 Element formatElement = doc.createElement(TIME_STAMP_OUTPUT_FORMAT_ELEMENT);
539 definitionElement.appendChild(formatElement);
540 formatElement.appendChild(doc.createTextNode(timeStampOutputFormat));
541
542 if (inputs != null) {
543 for (InputLine inputLine : inputs) {
544 definitionElement.appendChild(createInputLineElement(inputLine, doc));
545 }
546 }
547
548 if (outputs != null) {
549 for (OutputColumn output : outputs) {
550 Element outputColumnElement = doc.createElement(OUTPUT_COLUMN_ELEMENT);
551 definitionElement.appendChild(outputColumnElement);
552 outputColumnElement.setAttribute(NAME_ATTRIBUTE, output.name);
553 }
554 }
555
556 Transformer transformer = TransformerFactory.newInstance().newTransformer();
557 transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
558
559 // initialize StreamResult with File object to save to file
560 StreamResult result = new StreamResult(new StringWriter());
561 DOMSource source = new DOMSource(doc);
562 transformer.transform(source, result);
563 String xmlString = result.getWriter().toString();
564
565 try (FileWriter writer = new FileWriter(file);) {
566 writer.write(xmlString);
567 }
568
569 TmfTraceType.addCustomTraceType(CustomTxtTrace.class, categoryName, definitionName);
570
571 } catch (ParserConfigurationException e) {
572 Activator.logError("Error saving CustomTxtTraceDefinition: path=" + path, e); //$NON-NLS-1$
573 } catch (TransformerConfigurationException e) {
574 Activator.logError("Error saving CustomTxtTraceDefinition: path=" + path, e); //$NON-NLS-1$
575 } catch (TransformerFactoryConfigurationError e) {
576 Activator.logError("Error saving CustomTxtTraceDefinition: path=" + path, e); //$NON-NLS-1$
577 } catch (TransformerException e) {
578 Activator.logError("Error saving CustomTxtTraceDefinition: path=" + path, e); //$NON-NLS-1$
579 } catch (IOException e) {
580 Activator.logError("Error saving CustomTxtTraceDefinition: path=" + path, e); //$NON-NLS-1$
581 } catch (SAXException e) {
582 Activator.logError("Error saving CustomTxtTraceDefinition: path=" + path, e); //$NON-NLS-1$
583 }
584 }
585
586 private Element createInputLineElement(InputLine inputLine, Document doc) {
587 Element inputLineElement = doc.createElement(INPUT_LINE_ELEMENT);
588
589 Element cardinalityElement = doc.createElement(CARDINALITY_ELEMENT);
590 inputLineElement.appendChild(cardinalityElement);
591 cardinalityElement.setAttribute(MIN_ATTRIBUTE, Integer.toString(inputLine.cardinality.min));
592 cardinalityElement.setAttribute(MAX_ATTRIBUTE, Integer.toString(inputLine.cardinality.max));
593
594 Element regexElement = doc.createElement(REGEX_ELEMENT);
595 inputLineElement.appendChild(regexElement);
596 regexElement.appendChild(doc.createTextNode(inputLine.regex));
597
598 if (inputLine.columns != null) {
599 for (InputData inputData : inputLine.columns) {
600 Element inputDataElement = doc.createElement(INPUT_DATA_ELEMENT);
601 inputLineElement.appendChild(inputDataElement);
602 inputDataElement.setAttribute(NAME_ATTRIBUTE, inputData.name);
603 inputDataElement.setAttribute(ACTION_ATTRIBUTE, Integer.toString(inputData.action));
604 if (inputData.format != null) {
605 inputDataElement.setAttribute(FORMAT_ATTRIBUTE, inputData.format);
606 }
607 }
608 }
609
610 if (inputLine.childrenInputs != null) {
611 for (InputLine childInputLine : inputLine.childrenInputs) {
612 inputLineElement.appendChild(createInputLineElement(childInputLine, doc));
613 }
614 }
615
616 return inputLineElement;
617 }
618
619 /**
620 * Load all custom text trace definitions, including the user-defined and
621 * default (built-in) parsers.
622 *
623 * @return The loaded trace definitions
624 */
625 public static CustomTxtTraceDefinition[] loadAll() {
626 return loadAll(true);
627 }
628
629 /**
630 * Load all custom text trace definitions, including the user-defined and,
631 * optionally, the default (built-in) parsers.
632 *
633 * @param includeDefaults
634 * if true, the default (built-in) parsers are included
635 *
636 * @return The loaded trace definitions
637 * @since 3.2
638 */
639 public static CustomTxtTraceDefinition[] loadAll(boolean includeDefaults) {
640 File defaultFile = new File(CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME);
641 File legacyFileCore = new File(CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME_LEGACY_CORE);
642 File legacyFileUI = new File(CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME_LEGACY_UI);
643
644 /*
645 * If there is no file at the expected location, check the legacy
646 * locations instead.
647 */
648 if (!defaultFile.exists()) {
649 if (legacyFileCore.exists()) {
650 transferDefinitions(CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME_LEGACY_CORE);
651 } else if (legacyFileUI.exists()) {
652 transferDefinitions(CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME_LEGACY_UI);
653 }
654 }
655
656 Set<CustomTxtTraceDefinition> defs = new TreeSet<>(new Comparator<CustomTxtTraceDefinition>() {
657 @Override
658 public int compare(CustomTxtTraceDefinition o1, CustomTxtTraceDefinition o2) {
659 int result = o1.categoryName.compareTo(o2.categoryName);
660 if (result != 0) {
661 return result;
662 }
663 return o1.definitionName.compareTo(o2.definitionName);
664 }
665 });
666 defs.addAll(Arrays.asList(loadAll(CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME)));
667 if (includeDefaults) {
668 defs.addAll(Arrays.asList(loadAll(CUSTOM_TXT_TRACE_DEFINITIONS_DEFAULT_PATH_NAME)));
669 }
670 return defs.toArray(new CustomTxtTraceDefinition[0]);
671
672 }
673
674 private static void transferDefinitions(String defFile) {
675 CustomTxtTraceDefinition[] oldDefs = loadAll(defFile);
676 for (CustomTxtTraceDefinition def : oldDefs) {
677 /* Save in the new location */
678 def.save();
679 }
680 }
681
682 /**
683 * Load a specific text trace definition file.
684 *
685 * @param path
686 * The path to the file to load
687 * @return The loaded trace definitions
688 */
689 public static CustomTxtTraceDefinition[] loadAll(String path) {
690 try {
691 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
692 DocumentBuilder db = dbf.newDocumentBuilder();
693
694 // The following allows xml parsing without access to the dtd
695 db.setEntityResolver(createEmptyEntityResolver());
696
697 // The following catches xml parsing exceptions
698 db.setErrorHandler(createErrorHandler());
699
700 File file = new File(path);
701 if (!file.canRead()) {
702 return new CustomTxtTraceDefinition[0];
703 }
704 Document doc = db.parse(file);
705
706 Element root = doc.getDocumentElement();
707 if (!root.getNodeName().equals(CUSTOM_TXT_TRACE_DEFINITION_ROOT_ELEMENT)) {
708 return new CustomTxtTraceDefinition[0];
709 }
710
711 ArrayList<CustomTxtTraceDefinition> defList = new ArrayList<>();
712 NodeList nodeList = root.getChildNodes();
713 for (int i = 0; i < nodeList.getLength(); i++) {
714 Node node = nodeList.item(i);
715 if (node instanceof Element && node.getNodeName().equals(DEFINITION_ELEMENT)) {
716 CustomTxtTraceDefinition def = extractDefinition((Element) node);
717 if (def != null) {
718 defList.add(def);
719 }
720 }
721 }
722 return defList.toArray(new CustomTxtTraceDefinition[0]);
723 } catch (ParserConfigurationException e) {
724 Activator.logError("Error loading all in CustomTxtTraceDefinition: path=" + path, e); //$NON-NLS-1$
725 } catch (SAXException e) {
726 Activator.logError("Error loading all in CustomTxtTraceDefinition: path=" + path, e); //$NON-NLS-1$
727 } catch (IOException e) {
728 Activator.logError("Error loading all in CustomTxtTraceDefinition: path=" + path, e); //$NON-NLS-1$
729 }
730 return new CustomTxtTraceDefinition[0];
731 }
732
733 /**
734 * Load a single definition.
735 *
736 * @param definitionName
737 * Name of the definition to load
738 * @return The loaded trace definition
739 * @deprecated Use {@link #load(String, String)}
740 */
741 @Deprecated
742 public static CustomTxtTraceDefinition load(String definitionName) {
743 return load(CUSTOM_TXT_CATEGORY, definitionName);
744 }
745
746 /**
747 * Load a single definition.
748 *
749 * @param categoryName
750 * Category of the definition to load
751 * @param definitionName
752 * Name of the definition to load
753 * @return The loaded trace definition
754 * @since 3.2
755 */
756 public static CustomTxtTraceDefinition load(String categoryName, String definitionName) {
757 try {
758 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
759 DocumentBuilder db = dbf.newDocumentBuilder();
760
761 // The following allows xml parsing without access to the dtd
762 db.setEntityResolver(createEmptyEntityResolver());
763
764 // The following catches xml parsing exceptions
765 db.setErrorHandler(createErrorHandler());
766
767 CustomTxtTraceDefinition value = lookupDefinition(categoryName, definitionName, db, CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME);
768 if (value == null) {
769 return lookupDefinition(categoryName, definitionName, db, CUSTOM_TXT_TRACE_DEFINITIONS_DEFAULT_PATH_NAME);
770 }
771 return value;
772 } catch (ParserConfigurationException | SAXException | IOException e) {
773 Activator.logError("Error loading CustomTxtTraceDefinition: definitionName=" + definitionName, e); //$NON-NLS-1$
774 }
775 return null;
776 }
777
778 private static CustomTxtTraceDefinition lookupDefinition(String categoryName, String definitionName, DocumentBuilder db, String source) throws SAXException, IOException {
779 File file = new File(source);
780 if (!file.exists()) {
781 return null;
782 }
783 Document doc = db.parse(file);
784
785 Element root = doc.getDocumentElement();
786 if (!root.getNodeName().equals(CUSTOM_TXT_TRACE_DEFINITION_ROOT_ELEMENT)) {
787 return null;
788 }
789
790 Element definitionElement = findDefinitionElement(root, categoryName, definitionName);
791 if (definitionElement != null) {
792 return extractDefinition(definitionElement);
793 }
794 return null;
795 }
796
797 private static Element findDefinitionElement(Element root, String categoryName, String definitionName) {
798 NodeList nodeList = root.getChildNodes();
799 for (int i = 0; i < nodeList.getLength(); i++) {
800 Node node = nodeList.item(i);
801 if (node instanceof Element && node.getNodeName().equals(DEFINITION_ELEMENT)) {
802 Element element = (Element) node;
803 String categoryAttribute = element.getAttribute(CATEGORY_ATTRIBUTE);
804 if (categoryAttribute.isEmpty()) {
805 categoryAttribute = CUSTOM_TXT_CATEGORY;
806 }
807 String nameAttribute = element.getAttribute(NAME_ATTRIBUTE);
808 if (categoryName.equals(categoryAttribute) &&
809 definitionName.equals(nameAttribute)) {
810 return element;
811 }
812 }
813 }
814 return null;
815 }
816
817 /**
818 * Get the definition from a definition element.
819 *
820 * @param definitionElement
821 * The Element to extract from
822 * @return The loaded trace definition
823 */
824 public static CustomTxtTraceDefinition extractDefinition(Element definitionElement) {
825 CustomTxtTraceDefinition def = new CustomTxtTraceDefinition();
826
827 def.categoryName = definitionElement.getAttribute(CATEGORY_ATTRIBUTE);
828 if (def.categoryName.isEmpty()) {
829 def.categoryName = CUSTOM_TXT_CATEGORY;
830 }
831 def.definitionName = definitionElement.getAttribute(NAME_ATTRIBUTE);
832 if (def.definitionName.isEmpty()) {
833 return null;
834 }
835
836 NodeList nodeList = definitionElement.getChildNodes();
837 for (int i = 0; i < nodeList.getLength(); i++) {
838 Node node = nodeList.item(i);
839 String nodeName = node.getNodeName();
840 if (nodeName.equals(TIME_STAMP_OUTPUT_FORMAT_ELEMENT)) {
841 Element formatElement = (Element) node;
842 def.timeStampOutputFormat = formatElement.getTextContent();
843 } else if (nodeName.equals(INPUT_LINE_ELEMENT)) {
844 InputLine inputLine = extractInputLine((Element) node);
845 if (inputLine != null) {
846 def.inputs.add(inputLine);
847 }
848 } else if (nodeName.equals(OUTPUT_COLUMN_ELEMENT)) {
849 Element outputColumnElement = (Element) node;
850 OutputColumn outputColumn = new OutputColumn();
851 outputColumn.name = outputColumnElement.getAttribute(NAME_ATTRIBUTE);
852 def.outputs.add(outputColumn);
853 }
854 }
855 return def;
856 }
857
858 private static InputLine extractInputLine(Element inputLineElement) {
859 InputLine inputLine = new InputLine();
860 NodeList nodeList = inputLineElement.getChildNodes();
861 for (int i = 0; i < nodeList.getLength(); i++) {
862 Node node = nodeList.item(i);
863 String nodeName = node.getNodeName();
864 if (nodeName.equals(CARDINALITY_ELEMENT)) {
865 Element cardinalityElement = (Element) node;
866 try {
867 int min = Integer.parseInt(cardinalityElement.getAttribute(MIN_ATTRIBUTE));
868 int max = Integer.parseInt(cardinalityElement.getAttribute(MAX_ATTRIBUTE));
869 inputLine.cardinality = new Cardinality(min, max);
870 } catch (NumberFormatException e) {
871 return null;
872 }
873 } else if (nodeName.equals(REGEX_ELEMENT)) {
874 Element regexElement = (Element) node;
875 inputLine.regex = regexElement.getTextContent();
876 } else if (nodeName.equals(INPUT_DATA_ELEMENT)) {
877 Element inputDataElement = (Element) node;
878 InputData inputData = new InputData();
879 inputData.name = inputDataElement.getAttribute(NAME_ATTRIBUTE);
880 inputData.action = Integer.parseInt(inputDataElement.getAttribute(ACTION_ATTRIBUTE));
881 inputData.format = inputDataElement.getAttribute(FORMAT_ATTRIBUTE);
882 inputLine.addColumn(inputData);
883 } else if (nodeName.equals(INPUT_LINE_ELEMENT)) {
884 Element childInputLineElement = (Element) node;
885 InputLine childInputLine = extractInputLine(childInputLineElement);
886 if (childInputLine != null) {
887 inputLine.addChild(childInputLine);
888 }
889 }
890 }
891 return inputLine;
892 }
893
894 /**
895 * Delete a definition from the currently loaded ones.
896 *
897 * @param definitionName
898 * The name of the definition to delete
899 * @deprecated Use {@link #delete(String, String)}
900 */
901 @Deprecated
902 public static void delete(String definitionName) {
903 delete(CUSTOM_TXT_CATEGORY, definitionName);
904 }
905
906 /**
907 * Delete a definition from the currently loaded ones.
908 *
909 * @param categoryName
910 * The category of the definition to delete
911 * @param definitionName
912 * The name of the definition to delete
913 * @since 3.2
914 */
915 public static void delete(String categoryName, String definitionName) {
916 try {
917 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
918 DocumentBuilder db = dbf.newDocumentBuilder();
919
920 // The following allows xml parsing without access to the dtd
921 db.setEntityResolver(createEmptyEntityResolver());
922
923 // The following catches xml parsing exceptions
924 db.setErrorHandler(createErrorHandler());
925
926 File file = new File(CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME);
927 Document doc = db.parse(file);
928
929 Element root = doc.getDocumentElement();
930 if (!root.getNodeName().equals(CUSTOM_TXT_TRACE_DEFINITION_ROOT_ELEMENT)) {
931 return;
932 }
933
934 Element definitionElement = findDefinitionElement(root, categoryName, definitionName);
935 if (definitionElement != null) {
936 root.removeChild(definitionElement);
937 }
938
939 Transformer transformer = TransformerFactory.newInstance().newTransformer();
940 transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
941
942 // initialize StreamResult with File object to save to file
943 StreamResult result = new StreamResult(new StringWriter());
944 DOMSource source = new DOMSource(doc);
945 transformer.transform(source, result);
946 String xmlString = result.getWriter().toString();
947
948 try (FileWriter writer = new FileWriter(file);) {
949 writer.write(xmlString);
950 }
951
952 TmfTraceType.removeCustomTraceType(CustomTxtTrace.class, categoryName, definitionName);
953 // Check if default definition needs to be reloaded
954 TmfTraceType.addCustomTraceType(CustomTxtTrace.class, categoryName, definitionName);
955
956 } catch (ParserConfigurationException | SAXException | IOException | TransformerFactoryConfigurationError | TransformerException e) {
957 Activator.logError("Error deleting CustomTxtTraceDefinition: definitionName=" + definitionName, e); //$NON-NLS-1$
958 }
959 }
960 }
This page took 0.05582 seconds and 5 git commands to generate.