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