<commit_before>//
//  SPARQLClient.swift
//  Kineo
//
//  Created by <NAME> on 5/30/18.
//

import Foundation
import SPARQLSyntax

public struct SPARQLClient {
    var endpoint: URL
    var timeout: Double
    var silent: Bool
    
    public init(endpoint: URL, silent: Bool = false, timeout: Double = 5.0) {
        self.endpoint = endpoint
        self.timeout = timeout
        self.silent = silent
    }
    
    public func execute(_ query: String) throws -> QueryResult<[TermResult], [Triple]> {
        var args : (Data?, URLResponse?, Error?) = (nil, nil, nil)
        do {
            guard var components = URLComponents(string: endpoint.absoluteString) else {
                throw QueryError.evaluationError("Invalid URL components for SERVICE evaluation: \(endpoint)")
            }
            var queryItems = components.queryItems ?? []
            queryItems.append(URLQueryItem(name: "query", value: query))
            components.queryItems = queryItems
            
            guard let u = components.url else {
                throw QueryError.evaluationError("Invalid URL for SERVICE evaluation: \(components)")
            }
            
            let semaphore = DispatchSemaphore(value: 0)
            let session = URLSession.shared
            let task = session.dataTask(with: u) {
                args = ($0, $1, $2)
                semaphore.signal()
            }
            task.resume()
            
            _ = semaphore.wait(timeout: DispatchTime.now() + timeout)
            
            if let error = args.2 {
                throw QueryError.evaluationError("URL request failed: \(error)")
            }
            
            guard let data = args.0 else {
                throw QueryError.evaluationError("URL request did not return data")
            }
            
            guard let resp = args.1 else {
                throw QueryError.evaluationError("URL request did not return a response object")
            }
            
            do {
                let n = SPARQLContentNegotiator()
                let parser = n.negotiateParser(for: resp)
                return try parser.parse(data)
            } catch let e {
                throw QueryError.evaluationError("SPARQL Results XML parsing error: \(e)")
            }
        } catch let e {
            if silent {
                let results = [TermResult(bindings: [:])]
                return QueryResult.bindings([], results)
            } else {
                throw QueryError.evaluationError("SERVICE error: \(e)")
            }
        }
    }
}

public struct SPARQLContentNegotiator {
    public enum ResultFormat : String {
        case rdfxml = "http://www.w3.org/ns/formats/RDF_XML"
        case turtle = "http://www.w3.org/ns/formats/Turtle"
        case ntriples = "http://www.w3.org/ns/formats/N-Triples"
        case sparqlXML = "http://www.w3.org/ns/formats/SPARQL_Results_XML"
        case sparqlJSON = "http://www.w3.org/ns/formats/SPARQL_Results_JSON"
        case sparqlCSV = "http://www.w3.org/ns/formats/SPARQL_Results_CSV"
        case sparqlTSV = "http://www.w3.org/ns/formats/SPARQL_Results_TSV"
        
    }
    public let supportedSerializations : [ResultFormat] = [.sparqlXML, .sparqlJSON, .ntriples, .turtle]

    public init() {
    }

    public func negotiateSerializer<S : Sequence, B, T>(for result: QueryResult<B, T>, accept: S) -> SPARQLSerializable where S.Element == String {
        let xml = SPARQLXMLSerializer<TermResult>()
        let json = SPARQLJSONSerializer<TermResult>()
        let turtle = TurtleSerializer()
        let ntriples = NTriplesSerializer()
        switch result {
        case .bindings(_), .boolean(_):
            for a in accept {
                if a == "*/*" {
                    return xml
                } else if a.hasPrefix("application/sparql-results+json") {
                    return json
                } else if a.hasPrefix("application/json") {
                    return json
                } else if a.hasPrefix("text/plain") {
                    return json
                } else if a.hasPrefix("application/sparql-results+xml") {
                    return xml
                }
            }
            return xml
        case .triples(_):
            for a in accept {
                if a == "*/*" {
                    return turtle
                } else if a.hasPrefix("applicaiton/turtle") {
                    return turtle
                } else if a.hasPrefix("application/n-triples") {
                    return ntriples
                } else if a.hasPrefix("text/plain") {
                    return ntriples
                }
            }
            return ntriples
        }
    }
    
    public func negotiateParser(for response: URLResponse) -> SPARQLParsable {
        if let resp = response as? HTTPURLResponse {
            let type = resp.allHeaderFields["Content-Type"] as? String
            switch type {
            case "application/json", "application/sparql-results+json":
                return SPARQLJSONParser()
            case "application/sparql-results+xml":
                return SPARQLXMLParser()<commit_msg>Fix typo in SPARQLContentNegotiator.<commit_after>//
//  SPARQLClient.swift
//  Kineo
//
//  Created by <NAME> on 5/30/18.
//

import Foundation
import SPARQLSyntax

public struct SPARQLClient {
    var endpoint: URL
    var timeout: Double
    var silent: Bool
    
    public init(endpoint: URL, silent: Bool = false, timeout: Double = 5.0) {
        self.endpoint = endpoint
        self.timeout = timeout
        self.silent = silent
    }
    
    public func execute(_ query: String) throws -> QueryResult<[TermResult], [Triple]> {
        var args : (Data?, URLResponse?, Error?) = (nil, nil, nil)
        do {
            guard var components = URLComponents(string: endpoint.absoluteString) else {
                throw QueryError.evaluationError("Invalid URL components for SERVICE evaluation: \(endpoint)")
            }
            var queryItems = components.queryItems ?? []
            queryItems.append(URLQueryItem(name: "query", value: query))
            components.queryItems = queryItems
            
            guard let u = components.url else {
                throw QueryError.evaluationError("Invalid URL for SERVICE evaluation: \(components)")
            }
            
            let semaphore = DispatchSemaphore(value: 0)
            let session = URLSession.shared
            let task = session.dataTask(with: u) {
                args = ($0, $1, $2)
                semaphore.signal()
            }
            task.resume()
            
            _ = semaphore.wait(timeout: DispatchTime.now() + timeout)
            
            if let error = args.2 {
                throw QueryError.evaluationError("URL request failed: \(error)")
            }
            
            guard let data = args.0 else {
                throw QueryError.evaluationError("URL request did not return data")
            }
            
            guard let resp = args.1 else {
                throw QueryError.evaluationError("URL request did not return a response object")
            }
            
            do {
                let n = SPARQLContentNegotiator()
                let parser = n.negotiateParser(for: resp)
                return try parser.parse(data)
            } catch let e {
                throw QueryError.evaluationError("SPARQL Results XML parsing error: \(e)")
            }
        } catch let e {
            if silent {
                let results = [TermResult(bindings: [:])]
                return QueryResult.bindings([], results)
            } else {
                throw QueryError.evaluationError("SERVICE error: \(e)")
            }
        }
    }
}

public struct SPARQLContentNegotiator {
    public enum ResultFormat : String {
        case rdfxml = "http://www.w3.org/ns/formats/RDF_XML"
        case turtle = "http://www.w3.org/ns/formats/Turtle"
        case ntriples = "http://www.w3.org/ns/formats/N-Triples"
        case sparqlXML = "http://www.w3.org/ns/formats/SPARQL_Results_XML"
        case sparqlJSON = "http://www.w3.org/ns/formats/SPARQL_Results_JSON"
        case sparqlCSV = "http://www.w3.org/ns/formats/SPARQL_Results_CSV"
        case sparqlTSV = "http://www.w3.org/ns/formats/SPARQL_Results_TSV"
        
    }
    public let supportedSerializations : [ResultFormat] = [.sparqlXML, .sparqlJSON, .ntriples, .turtle]

    public init() {
    }

    public func negotiateSerializer<S : Sequence, B, T>(for result: QueryResult<B, T>, accept: S) -> SPARQLSerializable where S.Element == String {
        let xml = SPARQLXMLSerializer<TermResult>()
        let json = SPARQLJSONSerializer<TermResult>()
        let turtle = TurtleSerializer()
        let ntriples = NTriplesSerializer()
        switch result {
        case .bindings(_), .boolean(_):
            for a in accept {
                if a == "*/*" {
                    return xml
                } else if a.hasPrefix("application/sparql-results+json") {
                    return json
                } else if a.hasPrefix("application/json") {
                    return json
                } else if a.hasPrefix("text/plain") {
                    return json
                } else if a.hasPrefix("application/sparql-results+xml") {
                    return xml
                }
            }
            return xml
        case .triples(_):
            for a in accept {
                if a == "*/*" {
                    return turtle
                } else if a.hasPrefix("application/turtle") {
                    return turtle
                } else if a.hasPrefix("text/turtle") {
                    return turtle
                } else if a.hasPrefix("application/n-triples") {
                    return ntriples
                } else if a.hasPrefix("text/plain") {
                    return ntriples
                }
            }
            return ntriples
        }
    }
    
    public func negotiateParser(for response: URLResponse) -> SPARQLParsable {
        if let resp = response as? HTTPURLResponse {
            let type = resp.allHeaderFields["Content-Type"] as? String
            switch type {
            case "application/json", "application/sparql-results+json":
                return SPARQLJSONParser()
            case "application/sparql-results+xml":
                return SPARQLXMLParser()
{'fraction_non_alphanumeric': 0.09862253682800842, 'fraction_numerical': 0.004017600918308781, 'mean_word_length': 1.5904360753221012, 'pattern_counts': {'":': 4, '<': 15, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 14, 'xml': 14}, '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': '2750857', 'n_tokens_mistral': 2907, 'n_tokens_neox': 2778, 'n_words': 683}