/*******************************************************************************
 * Copyright (c) 2010, 2013 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
 *******************************************************************************/

package org.eclipse.linuxtools.internal.tmf.ui.parsers.custom;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

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.TransformerConfigurationException;
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.linuxtools.internal.tmf.ui.Activator;
import org.eclipse.linuxtools.internal.tmf.ui.Messages;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

/**
 * Trace definition for custom XML traces.
 *
 * @author Patrick Tassé
 */
public class CustomXmlTraceDefinition extends CustomTraceDefinition {

    /** "ignore" tag */
    public static final String TAG_IGNORE = Messages.CustomXmlTraceDefinition_ignoreTag;

    /** Name of the XML definitions file */
    protected static final String CUSTOM_XML_TRACE_DEFINITIONS_FILE_NAME = "custom_xml_parsers.xml"; //$NON-NLS-1$

    /** Path to the XML definitions file */
    protected static final String CUSTOM_XML_TRACE_DEFINITIONS_PATH_NAME =
        Activator.getDefault().getStateLocation().addTrailingSeparator().append(CUSTOM_XML_TRACE_DEFINITIONS_FILE_NAME).toString();

    private static final String CUSTOM_XML_TRACE_DEFINITION_ROOT_ELEMENT = Messages.CustomXmlTraceDefinition_definitionRootElement;
    private static final String DEFINITION_ELEMENT = Messages.CustomXmlTraceDefinition_definition;
    private static final String NAME_ATTRIBUTE = Messages.CustomXmlTraceDefinition_name;
    private static final String LOG_ENTRY_ATTRIBUTE = Messages.CustomXmlTraceDefinition_logEntry;
    private static final String TIME_STAMP_OUTPUT_FORMAT_ELEMENT = Messages.CustomXmlTraceDefinition_timestampOutputFormat;
    private static final String INPUT_ELEMENT_ELEMENT = Messages.CustomXmlTraceDefinition_inputElement;
    private static final String ATTRIBUTE_ELEMENT = Messages.CustomXmlTraceDefinition_attribute;
    private static final String INPUT_DATA_ELEMENT = Messages.CustomXmlTraceDefinition_inputData;
    private static final String ACTION_ATTRIBUTE = Messages.CustomXmlTraceDefinition_action;
    private static final String FORMAT_ATTRIBUTE = Messages.CustomXmlTraceDefinition_format;
    private static final String OUTPUT_COLUMN_ELEMENT = Messages.CustomXmlTraceDefinition_outputColumn;

    /** Top-level input element */
    public InputElement rootInputElement;

    /**
     * Default constructor
     */
    public CustomXmlTraceDefinition() {
        this("", null, new ArrayList<OutputColumn>(), ""); //$NON-NLS-1$ //$NON-NLS-2$
    }

    /**
     * Full constructor
     *
     * @param logtype
     *            Type of trace type
     * @param rootElement
     *            The top-level XML element
     * @param outputs
     *            The list of output columns
     * @param timeStampOutputFormat
     *            The timestamp format to use
     */
    public CustomXmlTraceDefinition(String logtype, InputElement rootElement,
            List<OutputColumn> outputs, String timeStampOutputFormat) {
        this.definitionName = logtype;
        this.rootInputElement = rootElement;
        this.outputs = outputs;
        this.timeStampOutputFormat = timeStampOutputFormat;
    }

    /**
     * Wrapper for input XML elements
     */
    public static class InputElement {

        /** Name of the element */
        public String elementName;

        /** Indicates if this is a log entry */
        public boolean logEntry;

        /** Name of the input element */
        public String inputName;

        /** Input action */
        public int inputAction;

        /** Input format */
        public String inputFormat;

        /** XML attributes of this element */
        public List<InputAttribute> attributes;

        /** Parent element */
        public InputElement parentElement;

        /** Following element in the file */
        public InputElement nextElement;

        /** Child elements */
        public List<InputElement> childElements;

        /**
         * Default (empty) constructor
         */
        public InputElement() {}

        /**
         * Constructor
         *
         * @param elementName
         *            Element name
         * @param logEntry
         *            If this element is a log entry
         * @param inputName
         *            Name of the the input
         * @param inputAction
         *            Input action
         * @param inputFormat
         *            Input format
         * @param attributes
         *            XML attributes of this element
         */
        public InputElement(String elementName, boolean logEntry,
                String inputName, int inputAction, String inputFormat,
                List<InputAttribute> attributes) {
            this.elementName = elementName;
            this.logEntry = logEntry;
            this.inputName = inputName;
            this.inputAction = inputAction;
            this.inputFormat = inputFormat;
            this.attributes = attributes;
        }

        /**
         * Add a XML attribute to the element
         *
         * @param attribute
         *            The attribute to add
         */
        public void addAttribute(InputAttribute attribute) {
            if (attributes == null) {
                attributes = new ArrayList<InputAttribute>(1);
            }
            attributes.add(attribute);
        }

        /**
         * Add a child element to this one.
         *
         * @param input
         *            The input element to add as child
         */
        public void addChild(InputElement input) {
            if (childElements == null) {
                childElements = new ArrayList<InputElement>(1);
            } else if (childElements.size() > 0) {
                InputElement last = childElements.get(childElements.size() - 1);
                last.nextElement = input;
            }
            childElements.add(input);
            input.parentElement = this;
        }

        /**
         * Set the following input element.
         *
         * @param input
         *            The input element to add as next element
         */
        public void addNext(InputElement input) {
            if (parentElement != null) {
                int index = parentElement.childElements.indexOf(this);
                parentElement.childElements.add(index + 1, input);
                InputElement next = nextElement;
                nextElement = input;
                input.nextElement = next;
            }
            input.parentElement = this.parentElement;
        }

        /**
         * Move this element up in its parent's list of children.
         */
        public void moveUp() {
            if (parentElement != null) {
                int index = parentElement.childElements.indexOf(this);
                if (index > 0) {
                    parentElement.childElements.add(index - 1 , parentElement.childElements.remove(index));
                    parentElement.childElements.get(index).nextElement = nextElement;
                    nextElement = parentElement.childElements.get(index);
                }
            }
        }

        /**
         * Move this element down in its parent's list of children.
         */
        public void moveDown() {
            if (parentElement != null) {
                int index = parentElement.childElements.indexOf(this);
                if (index < parentElement.childElements.size() - 1) {
                    parentElement.childElements.add(index + 1 , parentElement.childElements.remove(index));
                    nextElement = parentElement.childElements.get(index).nextElement;
                    parentElement.childElements.get(index).nextElement = this;
                }
            }
        }

    }

    /**
     * Wrapper for XML element attributes
     */
    public static class InputAttribute {

        /** Name of the XML attribute */
        public String attributeName;

        /** Input name */
        public String inputName;

        /** Input action */
        public int inputAction;

        /** Input format */
        public String inputFormat;

        /**
         * Default (empty) constructor
         */
        public InputAttribute() {}

        /**
         * Constructor
         *
         * @param attributeName
         *            Name of the XML attribute
         * @param inputName
         *            Input name
         * @param inputAction
         *            Input action
         * @param inputFormat
         *            Input format
         */
        public InputAttribute(String attributeName, String inputName,
                int inputAction, String inputFormat) {
            this.attributeName = attributeName;
            this.inputName = inputName;
            this.inputAction = inputAction;
            this.inputFormat = inputFormat;
        }
    }

    @Override
    public void save() {
        save(CUSTOM_XML_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
            EntityResolver resolver = new EntityResolver() {
                @Override
                public InputSource resolveEntity(String publicId, String systemId) {
                    String empty = ""; //$NON-NLS-1$
                    ByteArrayInputStream bais = new ByteArrayInputStream(empty.getBytes());
                    return new InputSource(bais);
                }
            };
            db.setEntityResolver(resolver);

            // The following catches xml parsing exceptions
            db.setErrorHandler(new ErrorHandler() {
                @Override
                public void error(SAXParseException saxparseexception) throws SAXException {}

                @Override
                public void warning(SAXParseException saxparseexception) throws SAXException {}

                @Override
                public void fatalError(SAXParseException saxparseexception) throws SAXException {
                    throw saxparseexception;
                }
            });

            Document doc = null;
            File file = new File(path);
            if (file.canRead()) {
                doc = db.parse(file);
                if (! doc.getDocumentElement().getNodeName().equals(CUSTOM_XML_TRACE_DEFINITION_ROOT_ELEMENT)) {
                    return;
                }
            } else {
                doc = db.newDocument();
                Node node = doc.createElement(CUSTOM_XML_TRACE_DEFINITION_ROOT_ELEMENT);
                doc.appendChild(node);
            }

            Element root = doc.getDocumentElement();

            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) &&
                        definitionName.equals(((Element) node).getAttribute(NAME_ATTRIBUTE))) {
                    root.removeChild(node);
                }
            }
            Element definitionElement = doc.createElement(DEFINITION_ELEMENT);
            root.appendChild(definitionElement);
            definitionElement.setAttribute(NAME_ATTRIBUTE, definitionName);

            Element formatElement = doc.createElement(TIME_STAMP_OUTPUT_FORMAT_ELEMENT);
            definitionElement.appendChild(formatElement);
            formatElement.appendChild(doc.createTextNode(timeStampOutputFormat));

            if (rootInputElement != null) {
                definitionElement.appendChild(createInputElementElement(rootInputElement, doc));
            }

            if (outputs != null) {
                for (OutputColumn output : outputs) {
                    Element outputColumnElement = doc.createElement(OUTPUT_COLUMN_ELEMENT);
                    definitionElement.appendChild(outputColumnElement);
                    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();

            FileWriter writer = new FileWriter(file);
            writer.write(xmlString);
            writer.close();
        } catch (ParserConfigurationException e) {
            Activator.getDefault().logError("Error saving CustomXmlTraceDefinition: path=" + path, e); //$NON-NLS-1$
        } catch (TransformerConfigurationException e) {
            Activator.getDefault().logError("Error saving CustomXmlTraceDefinition: path=" + path, e); //$NON-NLS-1$
        } catch (TransformerFactoryConfigurationError e) {
            Activator.getDefault().logError("Error saving CustomXmlTraceDefinition: path=" + path, e); //$NON-NLS-1$
        } catch (TransformerException e) {
            Activator.getDefault().logError("Error saving CustomXmlTraceDefinition: path=" + path, e); //$NON-NLS-1$
        } catch (IOException e) {
            Activator.getDefault().logError("Error saving CustomXmlTraceDefinition: path=" + path, e); //$NON-NLS-1$
        } catch (SAXException e) {
            Activator.getDefault().logError("Error saving CustomXmlTraceDefinition: path=" + path, e); //$NON-NLS-1$
        }
    }

    private Element createInputElementElement(InputElement inputElement, Document doc) {
        Element inputElementElement = doc.createElement(INPUT_ELEMENT_ELEMENT);
        inputElementElement.setAttribute(NAME_ATTRIBUTE, inputElement.elementName);

        if (inputElement.logEntry) {
            inputElementElement.setAttribute(LOG_ENTRY_ATTRIBUTE, Boolean.toString(inputElement.logEntry));
        }

        if (inputElement.parentElement != null) {
            Element inputDataElement = doc.createElement(INPUT_DATA_ELEMENT);
            inputElementElement.appendChild(inputDataElement);
            inputDataElement.setAttribute(NAME_ATTRIBUTE, inputElement.inputName);
            inputDataElement.setAttribute(ACTION_ATTRIBUTE, Integer.toString(inputElement.inputAction));
            if (inputElement.inputFormat != null) {
                inputDataElement.setAttribute(FORMAT_ATTRIBUTE, inputElement.inputFormat);
            }
        }

        if (inputElement.attributes != null) {
            for (InputAttribute attribute : inputElement.attributes) {
                Element inputAttributeElement = doc.createElement(ATTRIBUTE_ELEMENT);
                inputElementElement.appendChild(inputAttributeElement);
                inputAttributeElement.setAttribute(NAME_ATTRIBUTE, attribute.attributeName);
                Element inputDataElement = doc.createElement(INPUT_DATA_ELEMENT);
                inputAttributeElement.appendChild(inputDataElement);
                inputDataElement.setAttribute(NAME_ATTRIBUTE, attribute.inputName);
                inputDataElement.setAttribute(ACTION_ATTRIBUTE, Integer.toString(attribute.inputAction));
                if (attribute.inputFormat != null) {
                    inputDataElement.setAttribute(FORMAT_ATTRIBUTE, attribute.inputFormat);
                }
            }
        }

        if (inputElement.childElements != null) {
            for (InputElement childInputElement : inputElement.childElements) {
                inputElementElement.appendChild(createInputElementElement(childInputElement, doc));
            }
        }

        return inputElementElement;
    }

    /**
     * Load all the XML trace definitions in the default definitions file.
     *
     * @return The loaded trace definitions
     */
    public static CustomXmlTraceDefinition[] loadAll() {
        return loadAll(CUSTOM_XML_TRACE_DEFINITIONS_PATH_NAME);
    }

    /**
     * Load all the XML trace definitions in the given definitions file.
     *
     * @param path
     *            Path to the definitions file to load
     * @return The loaded trace definitions
     */
    public static CustomXmlTraceDefinition[] loadAll(String path) {
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();

            // The following allows xml parsing without access to the dtd
            EntityResolver resolver = new EntityResolver() {
                @Override
                public InputSource resolveEntity(String publicId, String systemId) {
                    String empty = ""; //$NON-NLS-1$
                    ByteArrayInputStream bais = new ByteArrayInputStream(empty.getBytes());
                    return new InputSource(bais);
                }
            };
            db.setEntityResolver(resolver);

            // The following catches xml parsing exceptions
            db.setErrorHandler(new ErrorHandler() {
                @Override
                public void error(SAXParseException saxparseexception) throws SAXException {}

                @Override
                public void warning(SAXParseException saxparseexception) throws SAXException {}

                @Override
                public void fatalError(SAXParseException saxparseexception) throws SAXException {
                    throw saxparseexception;
                }
            });

            File file = new File(path);
            if (!file.canRead()) {
                return new CustomXmlTraceDefinition[0];
            }
            Document doc = db.parse(file);

            Element root = doc.getDocumentElement();
            if (! root.getNodeName().equals(CUSTOM_XML_TRACE_DEFINITION_ROOT_ELEMENT)) {
                return new CustomXmlTraceDefinition[0];
            }

            ArrayList<CustomXmlTraceDefinition> defList = new ArrayList<CustomXmlTraceDefinition>();
            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)) {
                    CustomXmlTraceDefinition def = extractDefinition((Element) node);
                    if (def != null) {
                        defList.add(def);
                    }
                }
            }
            return defList.toArray(new CustomXmlTraceDefinition[0]);
        } catch (ParserConfigurationException e) {
            Activator.getDefault().logError("Error loading all in CustomXmlTraceDefinition: path=" + path, e); //$NON-NLS-1$
        } catch (SAXException e) {
            Activator.getDefault().logError("Error loading all in CustomXmlTraceDefinition: path=" + path, e); //$NON-NLS-1$
        } catch (IOException e) {
            Activator.getDefault().logError("Error loading all in CustomXmlTraceDefinition: path=" + path, e); //$NON-NLS-1$
        }
        return new CustomXmlTraceDefinition[0];
    }

    /**
     * Load the given trace definition.
     *
     * @param definitionName
     *            Name of the XML trace definition to load
     * @return The loaded trace definition
     */
    public static CustomXmlTraceDefinition load(String definitionName) {
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();

            // The following allows xml parsing without access to the dtd
            EntityResolver resolver = new EntityResolver() {
                @Override
                public InputSource resolveEntity(String publicId, String systemId) {
                    String empty = ""; //$NON-NLS-1$
                    ByteArrayInputStream bais = new ByteArrayInputStream(empty.getBytes());
                    return new InputSource(bais);
                }
            };
            db.setEntityResolver(resolver);

            // The following catches xml parsing exceptions
            db.setErrorHandler(new ErrorHandler() {
                @Override
                public void error(SAXParseException saxparseexception) throws SAXException {}

                @Override
                public void warning(SAXParseException saxparseexception) throws SAXException {}

                @Override
                public void fatalError(SAXParseException saxparseexception) throws SAXException {
                    throw saxparseexception;
                }
            });

            File file = new File(CUSTOM_XML_TRACE_DEFINITIONS_PATH_NAME);
            Document doc = db.parse(file);

            Element root = doc.getDocumentElement();
            if (! root.getNodeName().equals(CUSTOM_XML_TRACE_DEFINITION_ROOT_ELEMENT)) {
                return null;
            }

            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) &&
                        definitionName.equals(((Element) node).getAttribute(NAME_ATTRIBUTE))) {
                    return extractDefinition((Element) node);
                }
            }
        } catch (ParserConfigurationException e) {
            Activator.getDefault().logError("Error loading CustomXmlTraceDefinition: definitionName=" + definitionName, e); //$NON-NLS-1$
        } catch (SAXException e) {
            Activator.getDefault().logError("Error loading CustomXmlTraceDefinition: definitionName=" + definitionName, e); //$NON-NLS-1$
        } catch (IOException e) {
            Activator.getDefault().logError("Error loading CustomXmlTraceDefinition: definitionName=" + definitionName, e); //$NON-NLS-1$
        }
        return null;
    }

    /**
     * Extract a trace definition from an XML element.
     *
     * @param definitionElement
     *            Definition element
     * @return The extracted trace definition
     */
    public static CustomXmlTraceDefinition extractDefinition(Element definitionElement) {
        CustomXmlTraceDefinition def = new CustomXmlTraceDefinition();

        def.definitionName = definitionElement.getAttribute(NAME_ATTRIBUTE);
        if (def.definitionName == null) {
            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_ELEMENT_ELEMENT)) {
                InputElement inputElement = extractInputElement((Element) node);
                if (inputElement != null) {
                    if (def.rootInputElement == null) {
                        def.rootInputElement = inputElement;
                    } else {
                        return null;
                    }
                }
            } else if (nodeName.equals(OUTPUT_COLUMN_ELEMENT)) {
                Element outputColumnElement = (Element) node;
                OutputColumn outputColumn = new OutputColumn();
                outputColumn.name = outputColumnElement.getAttribute(NAME_ATTRIBUTE);
                def.outputs.add(outputColumn);
            }
        }
        return def;
    }

    private static InputElement extractInputElement(Element inputElementElement) {
        InputElement inputElement = new InputElement();
        inputElement.elementName = inputElementElement.getAttribute(NAME_ATTRIBUTE);
        inputElement.logEntry = (Boolean.toString(true).equals(inputElementElement.getAttribute(LOG_ENTRY_ATTRIBUTE))) ? true : false;
        NodeList nodeList = inputElementElement.getChildNodes();
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            String nodeName = node.getNodeName();
            if (nodeName.equals(INPUT_DATA_ELEMENT)) {
                Element inputDataElement = (Element) node;
                inputElement.inputName = inputDataElement.getAttribute(NAME_ATTRIBUTE);
                inputElement.inputAction = Integer.parseInt(inputDataElement.getAttribute(ACTION_ATTRIBUTE));
                inputElement.inputFormat = inputDataElement.getAttribute(FORMAT_ATTRIBUTE);
            } else if (nodeName.equals(ATTRIBUTE_ELEMENT)) {
                Element attributeElement = (Element) node;
                InputAttribute attribute = new InputAttribute();
                attribute.attributeName = attributeElement.getAttribute(NAME_ATTRIBUTE);
                NodeList attributeNodeList = attributeElement.getChildNodes();
                for (int j = 0; j < attributeNodeList.getLength(); j++) {
                    Node attributeNode = attributeNodeList.item(j);
                    String attributeNodeName = attributeNode.getNodeName();
                    if (attributeNodeName.equals(INPUT_DATA_ELEMENT)) {
                        Element inputDataElement = (Element) attributeNode;
                        attribute.inputName = inputDataElement.getAttribute(NAME_ATTRIBUTE);
                        attribute.inputAction = Integer.parseInt(inputDataElement.getAttribute(ACTION_ATTRIBUTE));
                        attribute.inputFormat = inputDataElement.getAttribute(FORMAT_ATTRIBUTE);
                    }
                }
                inputElement.addAttribute(attribute);
            } else if (nodeName.equals(INPUT_ELEMENT_ELEMENT)) {
                Element childInputElementElement = (Element) node;
                InputElement childInputElement = extractInputElement(childInputElementElement);
                if (childInputElement != null) {
                    inputElement.addChild(childInputElement);
                }
            }
        }
        return inputElement;
    }

    /**
     * Delete the given trace definition from the list of currently loaded ones.
     *
     * @param definitionName
     *            Name of the trace definition to delete
     */
    public static void delete(String definitionName) {
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();

            // The following allows xml parsing without access to the dtd
            EntityResolver resolver = new EntityResolver() {
                @Override
                public InputSource resolveEntity(String publicId, String systemId) {
                    String empty = ""; //$NON-NLS-1$
                    ByteArrayInputStream bais = new ByteArrayInputStream(empty.getBytes());
                    return new InputSource(bais);
                }
            };
            db.setEntityResolver(resolver);

            // The following catches xml parsing exceptions
            db.setErrorHandler(new ErrorHandler() {
                @Override
                public void error(SAXParseException saxparseexception) throws SAXException {}

                @Override
                public void warning(SAXParseException saxparseexception) throws SAXException {}

                @Override
                public void fatalError(SAXParseException saxparseexception) throws SAXException {
                    throw saxparseexception;
                }
            });

            File file = new File(CUSTOM_XML_TRACE_DEFINITIONS_PATH_NAME);
            Document doc = db.parse(file);

            Element root = doc.getDocumentElement();
            if (! root.getNodeName().equals(CUSTOM_XML_TRACE_DEFINITION_ROOT_ELEMENT)) {
                return;
            }

            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) &&
                        definitionName.equals(((Element) node).getAttribute(NAME_ATTRIBUTE))) {
                    root.removeChild(node);
                }
            }

            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();

            FileWriter writer = new FileWriter(file);
            writer.write(xmlString);
            writer.close();
        } catch (ParserConfigurationException e) {
            Activator.getDefault().logError("Error deleteing CustomXmlTraceDefinition: definitionName=" + definitionName, e); //$NON-NLS-1$
        } catch (SAXException e) {
            Activator.getDefault().logError("Error deleteing CustomXmlTraceDefinition: definitionName=" + definitionName, e); //$NON-NLS-1$
        } catch (IOException e) {
            Activator.getDefault().logError("Error deleteing CustomXmlTraceDefinition: definitionName=" + definitionName, e); //$NON-NLS-1$
        } catch (TransformerConfigurationException e) {
            Activator.getDefault().logError("Error deleteing CustomXmlTraceDefinition: definitionName=" + definitionName, e); //$NON-NLS-1$
        } catch (TransformerFactoryConfigurationError e) {
            Activator.getDefault().logError("Error deleteing CustomXmlTraceDefinition: definitionName=" + definitionName, e); //$NON-NLS-1$
        } catch (TransformerException e) {
            Activator.getDefault().logError("Error deleteing CustomXmlTraceDefinition: definitionName=" + definitionName, e); //$NON-NLS-1$
        }
    }
}
