/*******************************************************************************
* 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$
}
}
}