/******************************************************************************* * Copyright (c) 2010, 2016 Ericsson * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v1.0 which * accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Patrick Tasse - Initial API and implementation * Matthew Khouzam - Add support for default parsers *******************************************************************************/ package org.eclipse.tracecompass.tmf.core.parsers.custom; import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeSet; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.eclipse.core.runtime.Platform; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.tracecompass.internal.tmf.core.Activator; import org.eclipse.tracecompass.tmf.core.project.model.TmfTraceType; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /** * Trace definition for custom text traces. * * @author Patrick Tassé */ public class CustomTxtTraceDefinition extends CustomTraceDefinition { /** Input lines */ public List<InputLine> inputs; /** * Custom text label used internally and therefore should not be * externalized */ public static final String CUSTOM_TXT_CATEGORY = "Custom Text"; //$NON-NLS-1$ /** File name of the default definition file */ protected static final String CUSTOM_TXT_TRACE_DEFINITIONS_DEFAULT_FILE_NAME = "custom_txt_default_parsers.xml"; //$NON-NLS-1$ /** File name of the definition file */ protected static final String CUSTOM_TXT_TRACE_DEFINITIONS_FILE_NAME = "custom_txt_parsers.xml"; //$NON-NLS-1$ /** Path of the definition file */ protected static final String CUSTOM_TXT_TRACE_DEFINITIONS_DEFAULT_PATH_NAME = Platform.getInstallLocation().getURL().getPath() + "templates/org.eclipse.linuxtools.tmf.core/" + //$NON-NLS-1$ CUSTOM_TXT_TRACE_DEFINITIONS_DEFAULT_FILE_NAME; /** Path of the definition file */ protected static final String CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME = Activator.getDefault().getStateLocation().addTrailingSeparator().append(CUSTOM_TXT_TRACE_DEFINITIONS_FILE_NAME).toString(); /** * Legacy path to the XML definitions file (in the UI plug-in of linuxtools) TODO Remove * once we feel the transition phase is over. */ private static final String CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME_LEGACY_UI = Activator.getDefault().getStateLocation().removeLastSegments(1).addTrailingSeparator() .append("org.eclipse.linuxtools.tmf.ui") //$NON-NLS-1$ .append(CUSTOM_TXT_TRACE_DEFINITIONS_FILE_NAME).toString(); /** * Legacy path to the XML definitions file (in the core plug-in of linuxtools) TODO Remove * once we feel the transition phase is over. */ private static final String CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME_LEGACY_CORE = Activator.getDefault().getStateLocation().removeLastSegments(1).addTrailingSeparator() .append("org.eclipse.linuxtools.tmf.core") //$NON-NLS-1$ .append(CUSTOM_TXT_TRACE_DEFINITIONS_FILE_NAME).toString(); private static final String CUSTOM_TXT_TRACE_DEFINITION_ROOT_ELEMENT = Messages.CustomTxtTraceDefinition_definitionRootElement; private static final String DEFINITION_ELEMENT = Messages.CustomTxtTraceDefinition_definition; private static final String CATEGORY_ATTRIBUTE = Messages.CustomTxtTraceDefinition_category; private static final String TAG_ATTRIBUTE = Messages.CustomTxtTraceDefinition_tag; private static final String NAME_ATTRIBUTE = Messages.CustomTxtTraceDefinition_name; private static final String TIME_STAMP_OUTPUT_FORMAT_ELEMENT = Messages.CustomTxtTraceDefinition_timestampOutputFormat; private static final String INPUT_LINE_ELEMENT = Messages.CustomTxtTraceDefinition_inputLine; private static final String CARDINALITY_ELEMENT = Messages.CustomTxtTraceDefinition_cardinality; private static final String MIN_ATTRIBUTE = Messages.CustomTxtTraceDefinition_min; private static final String MAX_ATTRIBUTE = Messages.CustomTxtTraceDefinition_max; private static final String REGEX_ELEMENT = Messages.CustomTxtTraceDefinition_regEx; private static final String EVENT_TYPE_ELEMENT = Messages.CustomTxtTraceDefinition_eventType; private static final String INPUT_DATA_ELEMENT = Messages.CustomTxtTraceDefinition_inputData; private static final String ACTION_ATTRIBUTE = Messages.CustomTxtTraceDefinition_action; private static final String FORMAT_ATTRIBUTE = Messages.CustomTxtTraceDefinition_format; private static final String OUTPUT_COLUMN_ELEMENT = Messages.CustomTxtTraceDefinition_outputColumn; /** * This is the value that the extension sets for traceContentType to be able * to load an XML parser **/ private static final String TRACE_CONTENT_TYPE_ATTRIBUTE_VALUE = "text"; //$NON-NLS-1$ /** * Default constructor. */ public CustomTxtTraceDefinition() { this(CUSTOM_TXT_CATEGORY, "", new ArrayList<InputLine>(0), new ArrayList<OutputColumn>(0), ""); //$NON-NLS-1$ //$NON-NLS-2$ } /** * Full constructor. * * @param category * Category of the trace type * @param traceType * Name of the trace type * @param inputs * List of inputs * @param outputs * List of output columns * @param timeStampOutputFormat * The timestamp format to use */ public CustomTxtTraceDefinition(String category, String traceType, List<InputLine> inputs, List<OutputColumn> outputs, String timeStampOutputFormat) { this.categoryName = category; this.definitionName = traceType; this.inputs = inputs; this.outputs = outputs; this.timeStampOutputFormat = timeStampOutputFormat; } /** * Wrapper to store a line of the log file */ public static class InputLine { /** Data columns of this line */ public List<InputData> columns; /** Cardinality of this line (see {@link Cardinality}) */ public Cardinality cardinality; /** Parent line */ public InputLine parentInput; /** Level of this line */ public int level; /** Next input line in the file */ public InputLine nextInput; /** Children of this line (if one "event" spans many lines) */ public List<InputLine> childrenInputs; /** Event type associated with this line * @since 2.1*/ public String eventType; private String regex; private Pattern pattern; /** * Default (empty) constructor. */ public InputLine() { } /** * Constructor. * * @param cardinality * Cardinality of this line. * @param regex * Regex * @param columns * Columns to use */ public InputLine(Cardinality cardinality, String regex, List<InputData> columns) { this.cardinality = cardinality; this.regex = regex; this.columns = columns; } /** * Set the regex of this input line * * @param regex * Regex to set */ public void setRegex(String regex) { this.regex = regex; this.pattern = null; } /** * Get the current regex * * @return The current regex */ public String getRegex() { return regex; } /** * Get the Pattern object of this line's regex * * @return The Pattern * @throws PatternSyntaxException * If the regex does not parse correctly */ public Pattern getPattern() throws PatternSyntaxException { if (pattern == null) { pattern = Pattern.compile(regex); } return pattern; } /** * Add a child line to this line. * * @param input * The child input line */ public void addChild(InputLine input) { if (childrenInputs == null) { childrenInputs = new ArrayList<>(1); } else if (childrenInputs.size() > 0) { InputLine last = childrenInputs.get(childrenInputs.size() - 1); last.nextInput = input; } childrenInputs.add(input); input.parentInput = this; input.level = this.level + 1; } /** * Set the next input line. * * @param input * The next input line */ public void addNext(InputLine input) { if (parentInput != null) { int index = parentInput.childrenInputs.indexOf(this); parentInput.childrenInputs.add(index + 1, input); InputLine next = nextInput; nextInput = input; input.nextInput = next; } input.parentInput = this.parentInput; input.level = this.level; } /** * Move this line up in its parent's children. */ public void moveUp() { if (parentInput != null) { int index = parentInput.childrenInputs.indexOf(this); if (index > 0) { parentInput.childrenInputs.add(index - 1, parentInput.childrenInputs.remove(index)); parentInput.childrenInputs.get(index).nextInput = nextInput; nextInput = parentInput.childrenInputs.get(index); } } } /** * Move this line down in its parent's children. */ public void moveDown() { if (parentInput != null) { int index = parentInput.childrenInputs.indexOf(this); if (index < parentInput.childrenInputs.size() - 1) { parentInput.childrenInputs.add(index + 1, parentInput.childrenInputs.remove(index)); nextInput = parentInput.childrenInputs.get(index).nextInput; parentInput.childrenInputs.get(index).nextInput = this; } } } /** * Add a data column to this line * * @param column * The column to add */ public void addColumn(InputData column) { if (columns == null) { columns = new ArrayList<>(1); } columns.add(column); } /** * Get the next input lines. * * @param countMap * The map of line "sets". * @return The next list of lines. */ public List<InputLine> getNextInputs(Map<InputLine, Integer> countMap) { List<InputLine> nextInputs = new ArrayList<>(); InputLine next = nextInput; while (next != null) { nextInputs.add(next); if (next.cardinality.min > 0) { return nextInputs; } next = next.nextInput; } if (parentInput != null && parentInput.level > 0) { int parentCount = checkNotNull(countMap.get(parentInput)); if (parentCount < parentInput.getMaxCount()) { nextInputs.add(parentInput); } if (parentCount < parentInput.getMinCount()) { return nextInputs; } nextInputs.addAll(parentInput.getNextInputs(countMap)); } return nextInputs; } /** * Get the minimum possible amount of entries. * * @return The minimum */ public int getMinCount() { return cardinality.min; } /** * Get the maximum possible amount of entries. * * @return The maximum */ public int getMaxCount() { return cardinality.max; } @Override public String toString() { return regex + " " + cardinality; //$NON-NLS-1$ } } /** * Data column for input lines. */ public static class InputData { /** Tag of this input * @since 2.1*/ public Tag tag; /** Name of this input for "Other" tag */ public String name; /** Action id */ public int action; /** Format */ public String format; /** * Default (empty) constructor */ public InputData() { } /** * Full constructor * * @param name * Name * @param action * Action * @param format * Format */ public InputData(String name, int action, String format) { this.name = name; this.action = action; this.format = format; } /** * Constructor with default format * * @param name * Name * @param action * Action */ public InputData(String name, int action) { this.name = name; this.action = action; } /** * Constructor * * @param tag * Tag * @param action * Action * @since 2.1 */ public InputData(Tag tag, int action) { this.tag = tag; this.action = action; } } /** * Input line cardinality */ public static class Cardinality { /** Representation of infinity */ public static final int INF = Integer.MAX_VALUE; /** Preset for [1, 1] */ public static final Cardinality ONE = new Cardinality(1, 1); /** Preset for [1, inf] */ public static final Cardinality ONE_OR_MORE = new Cardinality(1, INF); /** Preset for [0, 1] */ public static final Cardinality ZERO_OR_ONE = new Cardinality(0, 1); /** Preset for [0, inf] */ public static final Cardinality ZERO_OR_MORE = new Cardinality(0, INF); private final int min; private final int max; /** * Constructor. * * @param min * Minimum * @param max * Maximum */ public Cardinality(int min, int max) { this.min = min; this.max = max; } @Override public String toString() { return "(" + (min >= 0 ? min : "?") + ',' + (max == INF ? "\u221E" : (max >= 0 ? max : "?")) + ')'; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + max; result = prime * result + min; return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof Cardinality)) { return false; } Cardinality other = (Cardinality) obj; return (this.min == other.min && this.max == other.max); } } @Override public void save() { save(CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME); } @Override public void save(String path) { try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); // The following allows xml parsing without access to the dtd db.setEntityResolver(createEmptyEntityResolver()); // The following catches xml parsing exceptions db.setErrorHandler(createErrorHandler()); Document doc = null; File file = new File(path); if (file.canRead()) { doc = db.parse(file); if (!doc.getDocumentElement().getNodeName().equals(CUSTOM_TXT_TRACE_DEFINITION_ROOT_ELEMENT)) { Activator.logError(String.format("Error saving CustomTxtTraceDefinition: path=%s is not a valid custom parser file", path)); //$NON-NLS-1$ return; } } else { doc = db.newDocument(); Node node = doc.createElement(CUSTOM_TXT_TRACE_DEFINITION_ROOT_ELEMENT); doc.appendChild(node); } Element root = doc.getDocumentElement(); Element oldDefinitionElement = findDefinitionElement(root, categoryName, definitionName); if (oldDefinitionElement != null) { root.removeChild(oldDefinitionElement); } Element definitionElement = doc.createElement(DEFINITION_ELEMENT); root.appendChild(definitionElement); definitionElement.setAttribute(CATEGORY_ATTRIBUTE, categoryName); definitionElement.setAttribute(NAME_ATTRIBUTE, definitionName); if (timeStampOutputFormat != null && !timeStampOutputFormat.isEmpty()) { Element formatElement = doc.createElement(TIME_STAMP_OUTPUT_FORMAT_ELEMENT); definitionElement.appendChild(formatElement); formatElement.appendChild(doc.createTextNode(timeStampOutputFormat)); } if (inputs != null) { for (InputLine inputLine : inputs) { definitionElement.appendChild(createInputLineElement(inputLine, doc)); } } if (outputs != null) { for (OutputColumn output : outputs) { Element outputColumnElement = doc.createElement(OUTPUT_COLUMN_ELEMENT); definitionElement.appendChild(outputColumnElement); outputColumnElement.setAttribute(TAG_ATTRIBUTE, output.tag.name()); outputColumnElement.setAttribute(NAME_ATTRIBUTE, output.name); } } Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$ // initialize StreamResult with File object to save to file StreamResult result = new StreamResult(new StringWriter()); DOMSource source = new DOMSource(doc); transformer.transform(source, result); String xmlString = result.getWriter().toString(); try (FileWriter writer = new FileWriter(file);) { writer.write(xmlString); } TmfTraceType.addCustomTraceType(CustomTxtTrace.class, categoryName, definitionName); } catch (ParserConfigurationException | TransformerFactoryConfigurationError | TransformerException | IOException | SAXException e) { Activator.logError("Error saving CustomTxtTraceDefinition: path=" + path, e); //$NON-NLS-1$ } } private Element createInputLineElement(InputLine inputLine, Document doc) { Element inputLineElement = doc.createElement(INPUT_LINE_ELEMENT); Element cardinalityElement = doc.createElement(CARDINALITY_ELEMENT); inputLineElement.appendChild(cardinalityElement); cardinalityElement.setAttribute(MIN_ATTRIBUTE, Integer.toString(inputLine.cardinality.min)); cardinalityElement.setAttribute(MAX_ATTRIBUTE, Integer.toString(inputLine.cardinality.max)); Element regexElement = doc.createElement(REGEX_ELEMENT); inputLineElement.appendChild(regexElement); regexElement.appendChild(doc.createTextNode(inputLine.regex)); if (inputLine.eventType != null) { Element eventTypeElement = doc.createElement(EVENT_TYPE_ELEMENT); inputLineElement.appendChild(eventTypeElement); eventTypeElement.appendChild(doc.createTextNode(inputLine.eventType)); } if (inputLine.columns != null) { for (InputData inputData : inputLine.columns) { Element inputDataElement = doc.createElement(INPUT_DATA_ELEMENT); inputLineElement.appendChild(inputDataElement); inputDataElement.setAttribute(TAG_ATTRIBUTE, inputData.tag.name()); inputDataElement.setAttribute(NAME_ATTRIBUTE, inputData.name); inputDataElement.setAttribute(ACTION_ATTRIBUTE, Integer.toString(inputData.action)); if (inputData.format != null) { inputDataElement.setAttribute(FORMAT_ATTRIBUTE, inputData.format); } } } if (inputLine.childrenInputs != null) { for (InputLine childInputLine : inputLine.childrenInputs) { inputLineElement.appendChild(createInputLineElement(childInputLine, doc)); } } return inputLineElement; } /** * Load all custom text trace definitions, including the user-defined and * default (built-in) parsers. * * @return The loaded trace definitions */ public static CustomTxtTraceDefinition[] loadAll() { return loadAll(true); } /** * Load all custom text trace definitions, including the user-defined and, * optionally, the default (built-in) parsers. * * @param includeDefaults * if true, the default (built-in) parsers are included * * @return The loaded trace definitions */ public static CustomTxtTraceDefinition[] loadAll(boolean includeDefaults) { File defaultFile = new File(CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME); File legacyFileCore = new File(CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME_LEGACY_CORE); File legacyFileUI = new File(CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME_LEGACY_UI); /* * If there is no file at the expected location, check the legacy * locations instead. */ if (!defaultFile.exists()) { if (legacyFileCore.exists()) { transferDefinitions(CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME_LEGACY_CORE); } else if (legacyFileUI.exists()) { transferDefinitions(CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME_LEGACY_UI); } } Set<CustomTxtTraceDefinition> defs = new TreeSet<>(new Comparator<CustomTxtTraceDefinition>() { @Override public int compare(CustomTxtTraceDefinition o1, CustomTxtTraceDefinition o2) { int result = o1.categoryName.compareTo(o2.categoryName); if (result != 0) { return result; } return o1.definitionName.compareTo(o2.definitionName); } }); defs.addAll(Arrays.asList(loadAll(CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME))); if (includeDefaults) { defs.addAll(Arrays.asList(loadAll(CUSTOM_TXT_TRACE_DEFINITIONS_DEFAULT_PATH_NAME))); // Also load definitions contributed by extensions Collection<String> paths = getExtensionDefinitionsPaths(TRACE_CONTENT_TYPE_ATTRIBUTE_VALUE); for (String customTraceDefinitionPath : paths) { defs.addAll(Arrays.asList(loadAll(customTraceDefinitionPath))); } } return defs.toArray(new CustomTxtTraceDefinition[0]); } private static void transferDefinitions(String defFile) { CustomTxtTraceDefinition[] oldDefs = loadAll(defFile); for (CustomTxtTraceDefinition def : oldDefs) { /* Save in the new location */ def.save(); } } /** * Load a specific text trace definition file. * * @param path * The path to the file to load * @return The loaded trace definitions */ public static CustomTxtTraceDefinition[] loadAll(String path) { try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); // The following allows xml parsing without access to the dtd db.setEntityResolver(createEmptyEntityResolver()); // The following catches xml parsing exceptions db.setErrorHandler(createErrorHandler()); File file = new File(path); if (!file.canRead()) { return new CustomTxtTraceDefinition[0]; } Document doc = db.parse(file); Element root = doc.getDocumentElement(); if (!root.getNodeName().equals(CUSTOM_TXT_TRACE_DEFINITION_ROOT_ELEMENT)) { return new CustomTxtTraceDefinition[0]; } ArrayList<CustomTxtTraceDefinition> defList = new ArrayList<>(); NodeList nodeList = root.getChildNodes(); for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); if (node instanceof Element && node.getNodeName().equals(DEFINITION_ELEMENT)) { CustomTxtTraceDefinition def = extractDefinition((Element) node); if (def != null) { defList.add(def); } } } return defList.toArray(new CustomTxtTraceDefinition[0]); } catch (ParserConfigurationException e) { Activator.logError("Error loading all in CustomTxtTraceDefinition: path=" + path, e); //$NON-NLS-1$ } catch (SAXException e) { Activator.logError("Error loading all in CustomTxtTraceDefinition: path=" + path, e); //$NON-NLS-1$ } catch (IOException e) { Activator.logError("Error loading all in CustomTxtTraceDefinition: path=" + path, e); //$NON-NLS-1$ } return new CustomTxtTraceDefinition[0]; } /** * Load a single definition. * * @param categoryName * Category of the definition to load * @param definitionName * Name of the definition to load * @return The loaded trace definition */ public static CustomTxtTraceDefinition load(String categoryName, String definitionName) { try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); // The following allows xml parsing without access to the dtd db.setEntityResolver(createEmptyEntityResolver()); // The following catches xml parsing exceptions db.setErrorHandler(createErrorHandler()); CustomTxtTraceDefinition value = lookupDefinition(categoryName, definitionName, db, CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME); if (value == null) { return lookupDefinition(categoryName, definitionName, db, CUSTOM_TXT_TRACE_DEFINITIONS_DEFAULT_PATH_NAME); } return value; } catch (ParserConfigurationException | SAXException | IOException e) { Activator.logError("Error loading CustomTxtTraceDefinition: definitionName=" + definitionName, e); //$NON-NLS-1$ } return null; } private static CustomTxtTraceDefinition lookupDefinition(String categoryName, String definitionName, DocumentBuilder db, String source) throws SAXException, IOException { File file = new File(source); if (!file.exists()) { return null; } Document doc = db.parse(file); Element root = doc.getDocumentElement(); if (!root.getNodeName().equals(CUSTOM_TXT_TRACE_DEFINITION_ROOT_ELEMENT)) { return null; } Element definitionElement = findDefinitionElement(root, categoryName, definitionName); if (definitionElement != null) { return extractDefinition(definitionElement); } return null; } private static Element findDefinitionElement(Element root, String categoryName, String definitionName) { NodeList nodeList = root.getChildNodes(); for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); if (node instanceof Element && node.getNodeName().equals(DEFINITION_ELEMENT)) { Element element = (Element) node; String categoryAttribute = element.getAttribute(CATEGORY_ATTRIBUTE); if (categoryAttribute.isEmpty()) { categoryAttribute = CUSTOM_TXT_CATEGORY; } String nameAttribute = element.getAttribute(NAME_ATTRIBUTE); if (categoryName.equals(categoryAttribute) && definitionName.equals(nameAttribute)) { return element; } } } return null; } /** * Get the definition from a definition element. * * @param definitionElement * The Element to extract from * @return The loaded trace definition */ public static CustomTxtTraceDefinition extractDefinition(Element definitionElement) { CustomTxtTraceDefinition def = new CustomTxtTraceDefinition(); def.categoryName = definitionElement.getAttribute(CATEGORY_ATTRIBUTE); if (def.categoryName.isEmpty()) { def.categoryName = CUSTOM_TXT_CATEGORY; } def.definitionName = definitionElement.getAttribute(NAME_ATTRIBUTE); if (def.definitionName.isEmpty()) { return null; } NodeList nodeList = definitionElement.getChildNodes(); for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); String nodeName = node.getNodeName(); if (nodeName.equals(TIME_STAMP_OUTPUT_FORMAT_ELEMENT)) { Element formatElement = (Element) node; def.timeStampOutputFormat = formatElement.getTextContent(); } else if (nodeName.equals(INPUT_LINE_ELEMENT)) { InputLine inputLine = extractInputLine((Element) node); if (inputLine != null) { def.inputs.add(inputLine); } } else if (nodeName.equals(OUTPUT_COLUMN_ELEMENT)) { Element outputColumnElement = (Element) node; Entry<@NonNull Tag, @NonNull String> entry = extractTagAndName(outputColumnElement, TAG_ATTRIBUTE, NAME_ATTRIBUTE); OutputColumn outputColumn = new OutputColumn(entry.getKey(), entry.getValue()); def.outputs.add(outputColumn); } } return def; } private static InputLine extractInputLine(Element inputLineElement) { InputLine inputLine = new InputLine(); NodeList nodeList = inputLineElement.getChildNodes(); for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); String nodeName = node.getNodeName(); if (nodeName.equals(CARDINALITY_ELEMENT)) { Element cardinalityElement = (Element) node; try { int min = Integer.parseInt(cardinalityElement.getAttribute(MIN_ATTRIBUTE)); int max = Integer.parseInt(cardinalityElement.getAttribute(MAX_ATTRIBUTE)); inputLine.cardinality = new Cardinality(min, max); } catch (NumberFormatException e) { return null; } } else if (nodeName.equals(REGEX_ELEMENT)) { Element regexElement = (Element) node; inputLine.regex = regexElement.getTextContent(); } else if (nodeName.equals(EVENT_TYPE_ELEMENT)) { Element eventTypeElement = (Element) node; inputLine.eventType = eventTypeElement.getTextContent(); } else if (nodeName.equals(INPUT_DATA_ELEMENT)) { Element inputDataElement = (Element) node; InputData inputData = new InputData(); Entry<@NonNull Tag, @NonNull String> entry = extractTagAndName(inputDataElement, TAG_ATTRIBUTE, NAME_ATTRIBUTE); inputData.tag = checkNotNull(entry.getKey()); inputData.name = checkNotNull(entry.getValue()); inputData.action = Integer.parseInt(inputDataElement.getAttribute(ACTION_ATTRIBUTE)); inputData.format = inputDataElement.getAttribute(FORMAT_ATTRIBUTE); inputLine.addColumn(inputData); } else if (nodeName.equals(INPUT_LINE_ELEMENT)) { Element childInputLineElement = (Element) node; InputLine childInputLine = extractInputLine(childInputLineElement); if (childInputLine != null) { inputLine.addChild(childInputLine); } } } return inputLine; } /** * Delete a definition from the currently loaded ones. * * @param categoryName * The category of the definition to delete * @param definitionName * The name of the definition to delete */ public static void delete(String categoryName, String definitionName) { try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); // The following allows xml parsing without access to the dtd db.setEntityResolver(createEmptyEntityResolver()); // The following catches xml parsing exceptions db.setErrorHandler(createErrorHandler()); File file = new File(CUSTOM_TXT_TRACE_DEFINITIONS_PATH_NAME); Document doc = db.parse(file); Element root = doc.getDocumentElement(); if (!root.getNodeName().equals(CUSTOM_TXT_TRACE_DEFINITION_ROOT_ELEMENT)) { return; } Element definitionElement = findDefinitionElement(root, categoryName, definitionName); if (definitionElement != null) { root.removeChild(definitionElement); } Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$ // initialize StreamResult with File object to save to file StreamResult result = new StreamResult(new StringWriter()); DOMSource source = new DOMSource(doc); transformer.transform(source, result); String xmlString = result.getWriter().toString(); try (FileWriter writer = new FileWriter(file);) { writer.write(xmlString); } TmfTraceType.removeCustomTraceType(CustomTxtTrace.class, categoryName, definitionName); // Check if default definition needs to be reloaded TmfTraceType.addCustomTraceType(CustomTxtTrace.class, categoryName, definitionName); } catch (ParserConfigurationException | SAXException | IOException | TransformerFactoryConfigurationError | TransformerException e) { Activator.logError("Error deleting CustomTxtTraceDefinition: definitionName=" + definitionName, e); //$NON-NLS-1$ } } }