<commit_before>"use strict";

var expat = require('node-expat');

class XMLUtils {
    constructor(definition) {
        this._definition = definition;
    }

    toXML(obj, rootName, indentation, optimizeEmpty, convertTypes = true) {
        return _toXML(obj, this._definition, rootName, indentation, optimizeEmpty, convertTypes);
    }

    fromXML(xml, inlineAttributes, convertTypes = true) {
        return _fromXML(xml, this._definition, inlineAttributes, convertTypes);
    }

    generateSample(rootName) {
        var result = {};
        result[rootName] =_generateSample(this._definition[rootName], rootName);
        return result;
    }
}

function _toXML (obj, definition, parentName, indentation, optimizeEmpty, convertTypes, level) {
    definition = definition ? definition : {};
    level = level ? level : 0;
    indentation = indentation ? indentation : 2;
    optimizeEmpty = optimizeEmpty ? true : false;

    var result = "";
    var namespace = "";
    var nsOffset = parentName.indexOf(':');
    if(nsOffset > - 1) {
        namespace = parentName.substr(0, nsOffset) + ":";
        parentName = parentName.substr(nsOffset + 1);

    } else if(definition[parentName + "$namespace"]) {
        namespace = definition[parentName + "$namespace"] + ":";
    }
    var attributes = definition[parentName + "$attributes"] || {};
    var order = obj[parentName + "$order"] || definition[parentName + "$order"];
    var type = obj[parentName + "$type"] || definition[parentName + "$type"];

    if(level === 0) {
        // Go into first level
        obj = obj[parentName];
    }

    var whitespace = " ".repeat(level * indentation);

    if(convertTypes && obj !== undefined && obj !== null) {
        if(type === "base64Binary") {
            obj = Buffer.from(obj).toString('base64');

        } else if(type === "hexBinary") {
            obj = Buffer.from(obj).toString('hex');
        }
    }

    if(typeof obj === undefined || obj === null) {
        // TODO: Handle null types

    } else if(Array.isArray(obj)) {
        obj.forEach(function(value) {
            result += _toXML(value, definition, namespace + parentName, indentation, optimizeEmpty, convertTypes, level);
        });

    } else if(typeof obj === "object") {
        var keys = Object.getOwnPropertyNames(obj);
        if(order) {
            keys = keys.sort(function(a, b) {
                return order.indexOf(a) - order.indexOf(b);
            });
        }

        let subResult = "";
        keys.forEach(function(key) {
            if(key === "$") {
                subResult += obj[key];

            } else if(key === "namespace$") {
                namespace = obj[key] + ":";

            } else if(key.indexOf("$") == 0) {
                attributes[key.substr(1)] = obj[key];

            } else if(key.indexOf("$") > 0) {
                // Skip definition information such as order

            } else {
                subResult += _toXML(obj[key], definition[parentName], key, indentation, optimizeEmpty, convertTypes, level + 1);
            }
        });

        // Generate start and end tag
        result += whitespace + "<" + namespace +  parentName;
        Object.getOwnPropertyNames(attributes).forEach(function(key){
            result += " " + key + '="' + attributes[key] + '"';
        });
        result += obj["$"] ? ">" + subResult : ">\n" + subResult + whitespace;
        result += "</" + namespace + parentName + ">" + (level > 0 ? "\n" : "");

    } else {
        result += whitespace + "<" + namespace +  parentName;
        Object.getOwnPropertyNames(attributes).forEach(function(key){
            result += " " + key + '="' + attributes[key] + '"';
        });

        if(obj === "" && optimizeEmpty) {
            result += " />\n";

        } else {
            result += ">" + escapeValue(obj) + "</" + namespace + parentName + ">\n";
        }
    }

    return result;
}

function escapeValue(value) {
    // https://stackoverflow.com/questions/1091945/what-characters-do-i-need-to-escape-in-xml-documents/1091953
    if(typeof value === 'string'){
        return value.replace(/((?:&(?!(?:apos|quot|[gl]t|amp);))|(?:<!--.+?-->)|(?:<!\[CDATA\[.+?]]>)|(?:<\?.+\?>)|[<>'"])/g, function(match, p1) {
            switch(p1) {
                case '>': return '&gt;';
                case '<': return '&lt;';
                case "'": return '&apos;';
                case '"': return '&quot;';
                case '&': return '&amp;';
                default: return p1;
            }
        });

    } else {
        return value;
    }
}

function _fromXML (xml, objectDefinition, inlineAttributes, convertTypes) {
    var definitions = [objectDefinition || {}];

    var parser = new expat.Parser('UTF-8');
    var result = {};
    var currentValue = "";
    var currentObject = result;
    var currentType = "";
    var objects = [];
    var names = [];

    var orders = [];

    parser.on('startElement', function(name, attributes) {
        // TODO: Handle namespaces in a nice way
        // Parse namespace<commit_msg>Only allow disabeling escaping when you inject a cdata tag first<commit_after>"use strict";

var expat = require('node-expat');

class XMLUtils {
    constructor(definition) {
        this._definition = definition;
    }

    toXML(obj, rootName, indentation, optimizeEmpty, convertTypes = true) {
        return _toXML(obj, this._definition, rootName, indentation, optimizeEmpty, convertTypes);
    }

    fromXML(xml, inlineAttributes, convertTypes = true) {
        return _fromXML(xml, this._definition, inlineAttributes, convertTypes);
    }

    generateSample(rootName) {
        var result = {};
        result[rootName] =_generateSample(this._definition[rootName], rootName);
        return result;
    }
}

function _toXML (obj, definition, parentName, indentation, optimizeEmpty, convertTypes, level) {
    definition = definition ? definition : {};
    level = level ? level : 0;
    indentation = indentation ? indentation : 2;
    optimizeEmpty = optimizeEmpty ? true : false;

    var result = "";
    var namespace = "";
    var nsOffset = parentName.indexOf(':');
    if(nsOffset > - 1) {
        namespace = parentName.substr(0, nsOffset) + ":";
        parentName = parentName.substr(nsOffset + 1);

    } else if(definition[parentName + "$namespace"]) {
        namespace = definition[parentName + "$namespace"] + ":";
    }
    var attributes = definition[parentName + "$attributes"] || {};
    var order = obj[parentName + "$order"] || definition[parentName + "$order"];
    var type = obj[parentName + "$type"] || definition[parentName + "$type"];

    if(level === 0) {
        // Go into first level
        obj = obj[parentName];
    }

    var whitespace = " ".repeat(level * indentation);

    if(convertTypes && obj !== undefined && obj !== null) {
        if(type === "base64Binary") {
            obj = Buffer.from(obj).toString('base64');

        } else if(type === "hexBinary") {
            obj = Buffer.from(obj).toString('hex');
        }
    }

    if(typeof obj === undefined || obj === null) {
        // TODO: Handle null types

    } else if(Array.isArray(obj)) {
        obj.forEach(function(value) {
            result += _toXML(value, definition, namespace + parentName, indentation, optimizeEmpty, convertTypes, level);
        });

    } else if(typeof obj === "object") {
        var keys = Object.getOwnPropertyNames(obj);
        if(order) {
            keys = keys.sort(function(a, b) {
                return order.indexOf(a) - order.indexOf(b);
            });
        }

        let subResult = "";
        keys.forEach(function(key) {
            if(key === "$") {
                subResult += obj[key];

            } else if(key === "namespace$") {
                namespace = obj[key] + ":";

            } else if(key.indexOf("$") == 0) {
                attributes[key.substr(1)] = obj[key];

            } else if(key.indexOf("$") > 0) {
                // Skip definition information such as order

            } else {
                subResult += _toXML(obj[key], definition[parentName], key, indentation, optimizeEmpty, convertTypes, level + 1);
            }
        });

        // Generate start and end tag
        result += whitespace + "<" + namespace +  parentName;
        Object.getOwnPropertyNames(attributes).forEach(function(key){
            result += " " + key + '="' + attributes[key] + '"';
        });
        result += obj["$"] ? ">" + subResult : ">\n" + subResult + whitespace;
        result += "</" + namespace + parentName + ">" + (level > 0 ? "\n" : "");

    } else {
        result += whitespace + "<" + namespace +  parentName;
        Object.getOwnPropertyNames(attributes).forEach(function(key){
            result += " " + key + '="' + attributes[key] + '"';
        });

        if(obj === "" && optimizeEmpty) {
            result += " />\n";

        } else {
            result += ">" + escapeValue(obj) + "</" + namespace + parentName + ">\n";
        }
    }

    return result;
}

function escapeValue(value) {
    // https://stackoverflow.com/questions/1091945/what-characters-do-i-need-to-escape-in-xml-documents/1091953
    if(typeof value === 'string'){
        return value.replace(/((?:&(?!(?:apos|quot|[gl]t|amp);))|(?:^<!\[CDATA\[.+?]]>)|[<>'"])/g, function(match, p1) {
            switch(p1) {
                case '>': return '&gt;';
                case '<': return '&lt;';
                case "'": return '&apos;';
                case '"': return '&quot;';
                case '&': return '&amp;';
                default: return p1;
            }
        });

    } else {
        return value;
    }
}

function _fromXML (xml, objectDefinition, inlineAttributes, convertTypes) {
    var definitions = [objectDefinition || {}];

    var parser = new expat.Parser('UTF-8');
    var result = {};
    var currentValue = "";
    var currentObject = result;
    var currentType = "";
    var objects = [];
    var names = [];

    var orders = [];

    parser.on('startElement', function(name, attributes) {
        // TODO: Handle namespaces in a nice way
        // Parse namespace
{'fraction_non_alphanumeric': 0.16509341199606686, 'fraction_numerical': 0.006489675516224189, 'mean_word_length': 2.236080178173719, 'pattern_counts': {'":': 8, '<': 19, '<?xml version=': 0, '>': 29, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 8}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '604992', 'n_tokens_mistral': 2981, 'n_tokens_neox': 2844, 'n_words': 639}