// // StringUtil.swift // SwifSoup // // Created by Nabil Chatbi on 20/04/16. // Copyright © 2016 Nabil Chatbi.. All rights reserved. // import Foundation /** * A minimal String utility class. Designed for internal jsoup use only. */ open class StringUtil { enum StringError: Error { case empty case short case error(String) } // memoised padding up to 10 fileprivate static let padding: [String] = ["", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "] private static let empty = "" private static let space = " " /** * Join a collection of strings by a seperator * @param strings collection of string objects * @param sep string to place between strings * @return joined string */ public static func join(_ strings: [String], sep: String) -> String { return strings.joined(separator: sep) } public static func join(_ strings: Set, sep: String) -> String { return strings.joined(separator: sep) } public static func join(_ strings: OrderedSet, sep: String) -> String { return strings.joined(separator: sep) } // /** // * Join a collection of strings by a seperator // * @param strings iterator of string objects // * @param sep string to place between strings // * @return joined string // */ // public static String join(Iterator strings, String sep) { // if (!strings.hasNext()) // return "" // // String start = strings.next().toString() // if (!strings.hasNext()) // only one, avoid builder // return start // // StringBuilder sb = new StringBuilder(64).append(start) // while (strings.hasNext()) { // sb.append(sep) // sb.append(strings.next()) // } // return sb.toString() // } /** * Returns space padding * @param width amount of padding desired * @return string of spaces * width */ public static func padding(_ width: Int) -> String { if width <= 0 { return empty } if width < padding.count { return padding[width] } return String.init(repeating: space, count: width) } /** * Tests if a string is blank: null, emtpy, or only whitespace (" ", \r\n, \t, etc) * @param string string to test * @return if string is blank */ public static func isBlank(_ string: String) -> Bool { if (string.count == 0) { return true } for chr in string { if (!StringUtil.isWhitespace(chr)) { return false } } return true } /** * Tests if a string is numeric, i.e. contains only digit characters * @param string string to test * @return true if only digit chars, false if empty or null or contains non-digit chrs */ public static func isNumeric(_ string: String) -> Bool { if (string.count == 0) { return false } for chr in string { if !("0"..."9" ~= chr) { return false } } return true } /** * Tests if a code point is "whitespace" as defined in the HTML spec. * @param c code point to test * @return true if code point is whitespace, false otherwise */ public static func isWhitespace(_ c: Character) -> Bool { //(c == " " || c == UnicodeScalar.BackslashT || c == "\n" || (c == "\f" ) || c == "\r") return c.isWhitespace } /** * Normalise the whitespace within this string; multiple spaces collapse to a single, and all whitespace characters * (e.g. newline, tab) convert to a simple space * @param string content to normalise * @return normalised string */ public static func normaliseWhitespace(_ string: String) -> String { let sb: StringBuilder = StringBuilder.init() appendNormalisedWhitespace(sb, string: string, stripLeading: false) return sb.toString() } /** * After normalizing the whitespace within a string, appends it to a string builder. * @param accum builder to append to * @param string string to normalize whitespace within * @param stripLeading set to true if you wish to remove any leading whitespace */ public static func appendNormalisedWhitespace(_ accum: StringBuilder, string: String, stripLeading: Bool ) { var lastWasWhite: Bool = false var reachedNonWhite: Bool = false for c in string { if (isWhitespace(c)) { if ((stripLeading && !reachedNonWhite) || lastWasWhite) { continue } accum.append(" ") lastWasWhite = true } else { accum.appendCodePoint(c) lastWasWhite = false reachedNonWhite = true } } } public static func inString(_ needle: String?, haystack: String...) -> Bool { return inString(needle, haystack) } public static func inString(_ needle: String?, _ haystack: [String?]) -> Bool { if(needle == nil) {return false} for hay in haystack { if(hay != nil && hay! == needle!) { return true } } return false } // open static func inSorted(_ needle: String, haystack: [String]) -> Bool { // return binarySearch(haystack, searchItem: needle) >= 0 // } // // open static func binarySearch(_ inputArr: Array, searchItem: T) -> Int { // var lowerIndex = 0 // var upperIndex = inputArr.count - 1 // // while (true) { // let currentIndex = (lowerIndex + upperIndex)/2 // if(inputArr[currentIndex] == searchItem) { // return currentIndex // } else if (lowerIndex > upperIndex) { // return -1 // } else { // if (inputArr[currentIndex] > searchItem) { // upperIndex = currentIndex - 1 // } else { // lowerIndex = currentIndex + 1 // } // } // } // } /** * Create a new absolute URL, from a provided existing absolute URL and a relative URL component. * @param base the existing absolulte base URL * @param relUrl the relative URL to resolve. (If it's already absolute, it will be returned) * @return the resolved absolute URL * @throws MalformedURLException if an error occurred generating the URL */ //NOTE: Not sure it work public static func resolve(_ base: URL, relUrl: String ) -> URL? { var base = base if(base.pathComponents.count == 0 && base.absoluteString.last != "/" && !base.isFileURL) { base = base.appendingPathComponent("/", isDirectory: false) } let u = URL(string: relUrl, relativeTo: base) return u } /** * Create a new absolute URL, from a provided existing absolute URL and a relative URL component. * @param baseUrl the existing absolute base URL * @param relUrl the relative URL to resolve. (If it's already absolute, it will be returned) * @return an absolute URL if one was able to be generated, or the empty string if not */ public static func resolve(_ baseUrl: String, relUrl: String ) -> String { let base = URL(string: baseUrl) if(base == nil || base?.scheme == nil) { let abs = URL(string: relUrl) return abs != nil && abs?.scheme != nil ? abs!.absoluteURL.absoluteString : empty } else { let url = resolve(base!, relUrl: relUrl) if(url != nil) { let ext = url!.absoluteURL.absoluteString return ext } if(base != nil && base?.scheme != nil) { let ext = base!.absoluteString return ext } return empty } // try { // try { // base = new URL(baseUrl) // } catch (MalformedURLException e) { // // the base is unsuitable, but the attribute/rel may be abs on its own, so try that // URL abs = new URL(relUrl) // return abs.toExternalForm() // } // return resolve(base, relUrl).toExternalForm() // } catch (MalformedURLException e) { // return "" // } } }