<commit_before>//
//  XML.swift
//  file-sync-services
//
//  Created by <NAME> on 4/15/17.
//
//

import Foundation

public struct XML: NativeTypesStructured {
    public let object: Any

    class ParserDelegate: NSObject {
        fileprivate var workingValues = [(key: String, value: Any?)]()
        fileprivate var object: Any? {
            return self.workingValues.first?.value
        }
    }

    public init(data: Data) throws {
        let parser = XMLParser(data: data)
        let delegate = ParserDelegate()
        parser.delegate = delegate
        guard parser.parse(), let object = delegate.object else {
            if let error = parser.parserError {
                throw XML.error("parsing xml", from: error)
            }
            else {
                throw XML.error("parsing xml", because: "of an unknown reason")
            }
        }
        self.object = object
    }

    public init(object: Any) {
        self.object = object
    }
}

extension XML.ParserDelegate: XMLParserDelegate {
    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
        if let last = self.workingValues.last, last.value is String {
            let _ = self.workingValues.popLast()
            self.workingValues.append((key: last.key, value: nil))
        }
        self.workingValues.append((key: elementName, value: nil))
    }

    func parserDidStartDocument(_ parser: XMLParser) {
        self.workingValues.append((key: "root", value: nil))
    }

    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
        guard let (finalKey, possibleFinalValue) = self.workingValues.popLast() else {
            return
        }

        guard let finalValue = possibleFinalValue
            , let (key, value) = self.workingValues.popLast()
            else
        {
            self.workingValues.append((finalKey, possibleFinalValue))
            return
        }

        switch value {
        case nil:
            self.workingValues.append((key, [finalKey:finalValue]))
        case var dict as [String:Any]:
            if let existingValue = dict[finalKey] {
                self.workingValues.append((key: key, value: [existingValue, finalValue]))
            }
            else {
                dict[finalKey] = finalValue
                self.workingValues.append((key: key, value: dict))
            }
        case var array as [Any]:
            array.append(finalValue)
            self.workingValues.append((key: key, value: array))
        case let string as String:
            self.workingValues.append((key: key, value: [string]))
        default:<commit_msg>[BF] Fix XML parsing with a mix of arrays and dictionaries<commit_after>//
//  XML.swift
//  file-sync-services
//
//  Created by <NAME> on 4/15/17.
//
//

import Foundation

public struct XML: NativeTypesStructured {
    public let object: Any

    class ParserDelegate: NSObject {
        fileprivate var workingValues = [(key: String, value: Any?)]()
        fileprivate var object: Any? {
            return self.workingValues.first?.value
        }
    }

    public init(data: Data) throws {
        let parser = XMLParser(data: data)
        let delegate = ParserDelegate()
        parser.delegate = delegate
        guard parser.parse(), let object = delegate.object else {
            if let error = parser.parserError {
                throw XML.error("parsing xml", from: error)
            }
            else {
                throw XML.error("parsing xml", because: "of an unknown reason")
            }
        }
        self.object = object
    }

    public init(object: Any) {
        self.object = object
    }
}

extension XML.ParserDelegate: XMLParserDelegate {
    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
        if let last = self.workingValues.last, last.value is String {
            let _ = self.workingValues.popLast()
            self.workingValues.append((key: last.key, value: nil))
        }
        self.workingValues.append((key: elementName, value: nil))
    }

    func parserDidStartDocument(_ parser: XMLParser) {
        self.workingValues.append((key: "root", value: nil))
    }

    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
        guard let (finalKey, possibleFinalValue) = self.workingValues.popLast() else {
            return
        }

        guard let finalValue = possibleFinalValue
            , let (key, value) = self.workingValues.popLast()
            else
        {
            self.workingValues.append((finalKey, possibleFinalValue))
            return
        }

        switch value {
        case nil:
            self.workingValues.append((key, [finalKey:finalValue]))
        case var dict as [String:Any]:
            if let existingValue = dict[finalKey] {
                if var array = existingValue as? [Any] {
                    array.append(finalValue)
                    dict[finalKey] = array
                }
                else {
                    dict[finalKey] = [existingValue, finalValue]
                }
                self.workingValues.append((key: key, value: dict))
            }
            else {
                dict[finalKey] = finalValue
                self.workingValues.append((key: key, value: dict))
            }
        case var array as [Any]:
            array.append(finalValue)
            self.workingValues.append((key: key, value: array))
        case let string as String:
            self.workingValues.append((key: key, value: [string]))
        default:
{'fraction_non_alphanumeric': 0.10148388197168685, 'fraction_numerical': 0.0017056114617090227, 'mean_word_length': 2.0056381342901077, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 4}, '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': '2436760', 'n_tokens_mistral': 1549, 'n_tokens_neox': 1504, 'n_words': 421}