This commit is contained in:
Nabil Chatbi 2016-12-28 14:22:28 +01:00
parent 6272c01cd8
commit db2f0bafbc
82 changed files with 4557 additions and 5103 deletions

View File

@ -13,7 +13,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
@ -41,6 +40,4 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}

View File

@ -23,7 +23,6 @@ class ViewController: UIViewController {
// Dispose of any resources that can be recreated.
}
func parseDocument()throws->Document {
let html = "<html><head><title>First parse</title></head>"
+ "<body><p>Parsed HTML into a doc.</p></body></html>"
@ -69,18 +68,16 @@ class ViewController: UIViewController {
" <foo />bar\n" +
" </body>\n" +
"</html>"
let doc: Document = try! SwiftSoup.parse(h);
let doc: Document = try! SwiftSoup.parse(h)
do {
for _ in 0...100000{
_ = try doc.select("div");
for _ in 0...100000 {
_ = try doc.select("div")
}
}
catch {
} catch {
}
}
func testSite()
{
func testSite() {
let myURLString = "http://apple.com"
guard let myURL = URL(string: myURLString) else {
print("Error: \(myURLString) doesn't seem to be a valid URL")
@ -90,16 +87,12 @@ class ViewController: UIViewController {
let doc: Document = try! SwiftSoup.parse(html)
do {
for _ in 0...100{
for _ in 0...100 {
_ = try doc.text()
}
}
catch {
} catch {
print("Error")
}
}
}

View File

@ -8,8 +8,7 @@
import Foundation
extension Array
{
extension Array {
func binarySearch<T: Comparable>(_ collection: [T], _ target: T) -> Int {
let min = 0
@ -19,7 +18,6 @@ extension Array
}
func binaryMakeGuess<T: Comparable>(min: Int, max: Int, target: T, collection: [T]) -> Int {
let guess = (min + max) / 2
@ -49,12 +47,8 @@ extension Array
}
}
extension Array where Element : Equatable
{
func lastIndexOf(_ e: Element) -> Int
{
extension Array where Element : Equatable {
func lastIndexOf(_ e: Element) -> Int {
for pos in (0..<self.count).reversed() {
let next = self[pos]
if (next == e) {
@ -64,5 +58,3 @@ extension Array where Element : Equatable
return -1
}
}

View File

@ -22,7 +22,7 @@ open class Attribute {
var key: String
var value: String
public init(key: String,value :String) throws {
public init(key: String, value: String) throws {
try Validate.notEmpty(string: key)
self.key = key.trim()
self.value = value
@ -32,7 +32,7 @@ open class Attribute {
Get the attribute key.
@return the attribute key
*/
open func getKey() -> String{
open func getKey() -> String {
return key
}
@ -68,7 +68,7 @@ open class Attribute {
Get the HTML representation of this attribute; e.g. {@code href="index.html"}.
@return HTML
*/
public func html()-> String {
public func html() -> String {
let accum = StringBuilder()
html(accum: accum, out: (Document("")).outputSettings())
return accum.toString()
@ -87,7 +87,7 @@ open class Attribute {
Get the string representation of this attribute, implemented as {@link #html()}.
@return string
*/
open func toString()-> String {
open func toString() -> String {
return html()
}
@ -118,38 +118,31 @@ open class Attribute {
&& isBooleanAttribute()
}
public func isBooleanAttribute() -> Bool
{
return (Attribute.booleanAttributes.binarySearch(Attribute.booleanAttributes,key) != -1)
public func isBooleanAttribute() -> Bool {
return (Attribute.booleanAttributes.binarySearch(Attribute.booleanAttributes, key) != -1)
}
public func hashCode() -> Int {
var result = key.hashValue
result = 31 * result + value.hashValue
return result
}
public func clone() -> Attribute
{
public func clone() -> Attribute {
do {
return try Attribute(key: key,value: value)
} catch Exception.Error( _ , let msg){
return try Attribute(key: key, value: value)
} catch Exception.Error( _, let msg) {
print(msg)
}catch{
} catch {
}
return try! Attribute(key: "",value: "")
return try! Attribute(key: "", value: "")
}
}
extension Attribute : Equatable
{
static public func == (lhs: Attribute, rhs: Attribute) -> Bool
{
extension Attribute : Equatable {
static public func == (lhs: Attribute, rhs: Attribute) -> Bool {
return lhs.value == rhs.value && lhs.key == rhs.key
}
}

View File

@ -21,15 +21,15 @@ import Foundation
*
* @author Jonathan Hedley, jonathan@hedley.net
*/
open class Attributes : NSCopying {
open class Attributes: NSCopying {
open static var dataPrefix : String = "data-"
open static var dataPrefix: String = "data-"
fileprivate var attributes : OrderedDictionary<String, Attribute> = OrderedDictionary<String, Attribute>()
fileprivate var attributes: OrderedDictionary<String, Attribute> = OrderedDictionary<String, Attribute>()
// linked hash map to preserve insertion order.
// null be default as so many elements have no attributes -- saves a good chunk of memory
public init(){}
public init() {}
/**
Get an attribute value by key.
@ -37,8 +37,8 @@ open class Attributes : NSCopying {
@return the attribute value if set; or empty string if not set.
@see #hasKey(String)
*/
open func get(key : String)-> String {
let attr : Attribute? = attributes.get(key:key)
open func get(key: String) -> String {
let attr: Attribute? = attributes.get(key:key)
return attr != nil ? attr!.getValue() : ""
}
@ -47,12 +47,11 @@ open class Attributes : NSCopying {
* @param key the attribute name
* @return the first matching attribute value if set; or empty string if not set.
*/
open func getIgnoreCase(key : String )throws -> String {
open func getIgnoreCase(key: String )throws -> String {
try Validate.notEmpty(string: key)
for attrKey in (attributes.keySet())
{
if attrKey.equalsIgnoreCase(string: key){
for attrKey in (attributes.keySet()) {
if attrKey.equalsIgnoreCase(string: key) {
return attributes.get(key: attrKey)!.getValue()
}
}
@ -64,7 +63,7 @@ open class Attributes : NSCopying {
@param key attribute key
@param value attribute value
*/
open func put(_ key : String , _ value : String) throws {
open func put(_ key: String, _ value: String) throws {
let attr = try Attribute(key: key, value: value)
put(attribute: attr)
}
@ -74,10 +73,10 @@ open class Attributes : NSCopying {
@param key attribute key
@param value attribute value
*/
open func put(_ key : String , _ value : Bool) throws {
if (value){
open func put(_ key: String, _ value: Bool) throws {
if (value) {
try put(attribute: BooleanAttribute(key: key))
}else{
} else {
try remove(key: key)
}
}
@ -86,7 +85,7 @@ open class Attributes : NSCopying {
Set a new attribute, or replace an existing one by key.
@param attribute attribute
*/
open func put(attribute : Attribute) {
open func put(attribute: Attribute) {
attributes.put(value: attribute, forKey:attribute.getKey())
}
@ -103,10 +102,10 @@ open class Attributes : NSCopying {
Remove an attribute by key. <b>Case insensitive.</b>
@param key attribute key to remove
*/
open func removeIgnoreCase(key : String ) throws {
open func removeIgnoreCase(key: String ) throws {
try Validate.notEmpty(string: key)
for attrKey in attributes.keySet(){
if (attrKey.equalsIgnoreCase(string: key)){
for attrKey in attributes.keySet() {
if (attrKey.equalsIgnoreCase(string: key)) {
attributes.remove(key: attrKey)
}
}
@ -117,7 +116,7 @@ open class Attributes : NSCopying {
@param key case-sensitive key to check for
@return true if key exists, false otherwise
*/
open func hasKey(key : String) -> Bool {
open func hasKey(key: String) -> Bool {
return attributes.containsKey(key: key)
}
@ -128,7 +127,7 @@ open class Attributes : NSCopying {
*/
open func hasKeyIgnoreCase(key: String) -> Bool {
for attrKey in attributes.keySet() {
if (attrKey.equalsIgnoreCase(string: key)){
if (attrKey.equalsIgnoreCase(string: key)) {
return true
}
}
@ -152,16 +151,15 @@ open class Attributes : NSCopying {
return
}
if (incoming.size() == 0){
if (incoming.size() == 0) {
return
}
attributes.putAll(all: incoming.attributes)
}
open func iterator() -> IndexingIterator<Array<Attribute>> {
if (attributes.isEmpty)
{
let args : [Attribute] = []
if (attributes.isEmpty) {
let args: [Attribute] = []
return args.makeIterator()
}
return attributes.orderedValues.makeIterator()
@ -173,7 +171,7 @@ open class Attributes : NSCopying {
@return an view of the attributes as a List.
*/
open func asList() -> Array<Attribute> {
var list : Array<Attribute> = Array(/*attributes.size()*/)
var list: Array<Attribute> = Array(/*attributes.size()*/)
for entry in attributes.orderedValues {
list.append(entry)
}
@ -186,11 +184,11 @@ open class Attributes : NSCopying {
* @return map of custom data attributes.
*/
//Map<String, String>
open func dataset() -> Dictionary<String,String> {
var dataset = Dictionary<String,String>()
for attribute in attributes{
open func dataset() -> Dictionary<String, String> {
var dataset = Dictionary<String, String>()
for attribute in attributes {
let attr = attribute.1
if(attr.isDataAttribute()){
if(attr.isDataAttribute()) {
let key = attr.getKey().substring(Attributes.dataPrefix.characters.count)
dataset[key] = attribute.1.getValue()
}
@ -209,7 +207,7 @@ open class Attributes : NSCopying {
return accum.toString()
}
public func html(accum: StringBuilder,out: OutputSettings ) throws {
public func html(accum: StringBuilder, out: OutputSettings ) throws {
for attribute in attributes.orderedValues {
accum.append(" ")
attribute.html(accum: accum, out: out)
@ -226,9 +224,9 @@ open class Attributes : NSCopying {
* @return if both sets of attributes have the same content
*/
open func equals(o: AnyObject?) -> Bool {
if(o == nil){return false}
if(o == nil) {return false}
if (self === o.self) {return true}
guard let that : Attributes = o as? Attributes else {return false}
guard let that: Attributes = o as? Attributes else {return false}
return (attributes == that.attributes)
}
@ -240,8 +238,7 @@ open class Attributes : NSCopying {
return attributes.hashCode()
}
public func copy(with zone: NSZone? = nil) -> Any
{
public func copy(with zone: NSZone? = nil) -> Any {
let clone = Attributes()
clone.attributes = attributes.clone()
return clone
@ -260,9 +257,8 @@ open class Attributes : NSCopying {
extension Attributes : Sequence {
public func makeIterator() -> AnyIterator<Attribute> {
var list = attributes.orderedValues
return AnyIterator{
return AnyIterator {
return list.count > 0 ? list.removeFirst() : nil
}
}
}

View File

@ -11,16 +11,15 @@ import Foundation
/**
* A boolean attribute that is written out without any value.
*/
open class BooleanAttribute : Attribute {
open class BooleanAttribute: Attribute {
/**
* Create a new boolean attribute from unencoded (raw) key.
* @param key attribute key
*/
init(key : String) throws {
init(key: String) throws {
try super.init(key: key, value: "")
}
override public func isBooleanAttribute() -> Bool {
return true
}

View File

@ -17,10 +17,10 @@ private let digitSet = CharacterSet.decimalDigits
extension Character {
public static let BackslashF : Character = Character(UnicodeScalar(12))
public static let BackslashF: Character = Character(UnicodeScalar(12))
//http://www.unicode.org/glossary/#supplementary_code_point
public static let MIN_SUPPLEMENTARY_CODE_POINT : UInt32 = 0x010000
public static let MIN_SUPPLEMENTARY_CODE_POINT: UInt32 = 0x010000
/// The first `UnicodeScalar` of `self`.
var unicodeScalar: UnicodeScalar {
@ -48,7 +48,7 @@ extension Character {
switch self {
case " ", "\t", "\n", "\r", "\r\n",Character.BackslashF: return true
case " ", "\t", "\n", "\r", "\r\n", Character.BackslashF: return true
case "\u{000C}", "\u{000B}", "\u{0085}": return true // Form Feed, vertical tab, next line (nel)
@ -170,8 +170,7 @@ extension Character {
return Character(UnicodeScalar(value)!)
}
func unicodeScalarCodePoint() -> UInt32
{
func unicodeScalarCodePoint() -> UInt32 {
return unicodeScalar.value
}
@ -179,22 +178,18 @@ extension Character {
return codePoint >= MIN_SUPPLEMENTARY_CODE_POINT ? 2 : 1
}
static func isLetter(_ char: Character)->Bool{
static func isLetter(_ char: Character) -> Bool {
return char.isLetter()
}
func isLetter()->Bool{
func isLetter() -> Bool {
return self.isMemberOfCharacterSet(CharacterSet.letters)
}
static func isLetterOrDigit(_ char: Character)->Bool
{
static func isLetterOrDigit(_ char: Character) -> Bool {
return char.isLetterOrDigit()
}
func isLetterOrDigit()->Bool
{
func isLetterOrDigit() -> Bool {
if(self.isLetter()) {return true}
return self.isDigit
}
}

View File

@ -11,18 +11,16 @@ import Foundation
/**
CharacterReader consumes tokens off a string. To replace the old TokenQueue.
*/
public final class CharacterReader
{
public static let EOF : UnicodeScalar = "\u{FFFF}"//65535
private static let maxCacheLen : Int = 12
private let input : [UnicodeScalar]
private let length : Int
private var pos : Int = 0
private var mark : Int = 0
private let stringCache : Array<String?> // holds reused strings in this doc, to lessen garbage
public final class CharacterReader {
public static let EOF: UnicodeScalar = "\u{FFFF}"//65535
private static let maxCacheLen: Int = 12
private let input: [UnicodeScalar]
private let length: Int
private var pos: Int = 0
private var mark: Int = 0
private let stringCache: Array<String?> // holds reused strings in this doc, to lessen garbage
public init(_ input: String)
{
public init(_ input: String) {
self.input = Array(input.unicodeScalars)
self.length = self.input.count
stringCache = Array(repeating:nil, count:512)
@ -75,10 +73,10 @@ public final class CharacterReader
* @param c scan target
* @return offset between current position and next instance of target. -1 if not found.
*/
public func nextIndexOf(_ c : UnicodeScalar) -> Int {
public func nextIndexOf(_ c: UnicodeScalar) -> Int {
// doesn't handle scanning for surrogates
for i in pos..<length {
if (c == input[i]){
if (c == input[i]) {
return i - pos
}
}
@ -93,25 +91,24 @@ public final class CharacterReader
*/
public func nextIndexOf(_ seq: String) -> Int {
// doesn't handle scanning for surrogates
if(seq.isEmpty){return -1}
let startChar : UnicodeScalar = seq.unicodeScalar(0)
if(seq.isEmpty) {return -1}
let startChar: UnicodeScalar = seq.unicodeScalar(0)
for var offset in pos..<length {
// scan to first instance of startchar:
if (startChar != input[offset]){
if (startChar != input[offset]) {
offset+=1
while(offset < length && startChar != input[offset]) { offset+=1 }
}
var i = offset + 1
let last = i + seq.unicodeScalars.count-1
if (offset < length && last <= length)
{
if (offset < length && last <= length) {
var j = 1
while i < last && seq.unicodeScalar(j) == input[i] {
j+=1
i+=1
}
// found full sequence
if (i == last){
if (i == last) {
return offset - pos
}
}
@ -119,7 +116,7 @@ public final class CharacterReader
return -1
}
public func consumeTo(_ c : UnicodeScalar) -> String {
public func consumeTo(_ c: UnicodeScalar) -> String {
let offset = nextIndexOf(c)
if (offset != -1) {
let consumed = cacheString(pos, offset)
@ -141,24 +138,23 @@ public final class CharacterReader
}
}
public func consumeToAny(_ chars: UnicodeScalar...)->String {
public func consumeToAny(_ chars: UnicodeScalar...) -> String {
return consumeToAny(chars)
}
public func consumeToAny(_ chars: [UnicodeScalar])->String {
let start : Int = pos
let remaining : Int = length
public func consumeToAny(_ chars: [UnicodeScalar]) -> String {
let start: Int = pos
let remaining: Int = length
let val = input
if(start == 2528){
if(start == 2528) {
let d = 1
print(d)
}
OUTER: while (pos < remaining)
{
if(pos == 41708){
OUTER: while (pos < remaining) {
if(pos == 41708) {
let d = 1
print(d)
}
if chars.contains(val[pos]){
if chars.contains(val[pos]) {
break OUTER
}
// for c in chars {
@ -172,17 +168,16 @@ public final class CharacterReader
return pos > start ? cacheString(start, pos-start) : ""
}
public func consumeToAnySorted(_ chars: UnicodeScalar...)->String {
public func consumeToAnySorted(_ chars: UnicodeScalar...) -> String {
return consumeToAnySorted(chars)
}
public func consumeToAnySorted(_ chars: [UnicodeScalar])->String {
public func consumeToAnySorted(_ chars: [UnicodeScalar]) -> String {
let start = pos
let remaining = length
let val = input
while (pos < remaining) {
if (chars.binarySearch(chars, val[pos]) >= 0){
if (chars.binarySearch(chars, val[pos]) >= 0) {
break
}
pos += 1
@ -191,8 +186,6 @@ public final class CharacterReader
return pos > start ? cacheString(start, pos-start) : ""
}
public func consumeData() -> String {
// &, <, null
let start = pos
@ -200,8 +193,8 @@ public final class CharacterReader
let val = input
while (pos < remaining) {
let c : UnicodeScalar = val[pos]
if (c == "&" || c == "<" || c == TokeniserStateVars.nullScalr){
let c: UnicodeScalar = val[pos]
if (c == "&" || c == "<" || c == TokeniserStateVars.nullScalr) {
break
}
pos += 1
@ -210,15 +203,15 @@ public final class CharacterReader
return pos > start ? cacheString(start, pos-start) : ""
}
public func consumeTagName()-> String {
public func consumeTagName() -> String {
// '\t', '\n', '\r', '\f', ' ', '/', '>', nullChar
let start = pos
let remaining = length
let val = input
while (pos < remaining) {
let c : UnicodeScalar = val[pos]
if (c == "\t" || c == "\n" || c == "\r" || c == UnicodeScalar.BackslashF || c == " " || c == "/" || c == ">" || c == TokeniserStateVars.nullScalr){
let c: UnicodeScalar = val[pos]
if (c == "\t" || c == "\n" || c == "\r" || c == UnicodeScalar.BackslashF || c == " " || c == "/" || c == ">" || c == TokeniserStateVars.nullScalr) {
break
}
pos += 1
@ -226,41 +219,40 @@ public final class CharacterReader
return pos > start ? cacheString(start, pos-start) : ""
}
public func consumeToEnd()-> String {
public func consumeToEnd() -> String {
let data = cacheString(pos, length-pos)
pos = length
return data
}
public func consumeLetterSequence()-> String {
public func consumeLetterSequence() -> String {
let start = pos
while (pos < length) {
let c : UnicodeScalar = input[pos]
if ((c >= "A" && c <= "Z") || (c >= "a" && c <= "z") || c.isMemberOfCharacterSet(CharacterSet.letters)){
let c: UnicodeScalar = input[pos]
if ((c >= "A" && c <= "Z") || (c >= "a" && c <= "z") || c.isMemberOfCharacterSet(CharacterSet.letters)) {
pos += 1
}else{
} else {
break
}
}
return cacheString(start, pos - start)
}
public func consumeLetterThenDigitSequence()-> String {
public func consumeLetterThenDigitSequence() -> String {
let start = pos
while (pos < length) {
let c = input[pos]
if ((c >= "A" && c <= "Z") || (c >= "a" && c <= "z") || c.isMemberOfCharacterSet(CharacterSet.letters)){
if ((c >= "A" && c <= "Z") || (c >= "a" && c <= "z") || c.isMemberOfCharacterSet(CharacterSet.letters)) {
pos += 1
}else{
} else {
break
}
}
while (!isEmpty()) {
let c = input[pos]
if (c >= "0" && c <= "9"){
if (c >= "0" && c <= "9") {
pos += 1
}else{
} else {
break
}
}
@ -268,13 +260,13 @@ public final class CharacterReader
return cacheString(start, pos - start)
}
public func consumeHexSequence()-> String {
public func consumeHexSequence() -> String {
let start = pos
while (pos < length) {
let c = input[pos]
if ((c >= "0" && c <= "9") || (c >= "A" && c <= "F") || (c >= "a" && c <= "f")){
if ((c >= "0" && c <= "9") || (c >= "A" && c <= "F") || (c >= "a" && c <= "f")) {
pos+=1
}else{
} else {
break
}
}
@ -285,9 +277,9 @@ public final class CharacterReader
let start = pos
while (pos < length) {
let c = input[pos]
if (c >= "0" && c <= "9"){
if (c >= "0" && c <= "9") {
pos+=1
}else{
} else {
break
}
}
@ -299,68 +291,68 @@ public final class CharacterReader
}
public func matches(_ seq: String)-> Bool {
public func matches(_ seq: String) -> Bool {
let scanLength = seq.unicodeScalars.count
if (scanLength > length - pos){
if (scanLength > length - pos) {
return false
}
for offset in 0..<scanLength{
if (seq.unicodeScalar(offset) != input[pos+offset]){
for offset in 0..<scanLength {
if (seq.unicodeScalar(offset) != input[pos+offset]) {
return false
}
}
return true
}
public func matchesIgnoreCase(_ seq: String )->Bool {
public func matchesIgnoreCase(_ seq: String ) -> Bool {
let scanLength = seq.unicodeScalars.count
if(scanLength == 0){
if(scanLength == 0) {
return false
}
if (scanLength > length - pos){
if (scanLength > length - pos) {
return false
}
for offset in 0..<scanLength{
let upScan : UnicodeScalar = seq.unicodeScalar(offset).uppercase
let upTarget : UnicodeScalar = input[pos+offset].uppercase
if (upScan != upTarget){
for offset in 0..<scanLength {
let upScan: UnicodeScalar = seq.unicodeScalar(offset).uppercase
let upTarget: UnicodeScalar = input[pos+offset].uppercase
if (upScan != upTarget) {
return false
}
}
return true
}
public func matchesAny(_ seq: UnicodeScalar...)->Bool {
if (isEmpty()){
public func matchesAny(_ seq: UnicodeScalar...) -> Bool {
if (isEmpty()) {
return false
}
let c : UnicodeScalar = input[pos]
let c: UnicodeScalar = input[pos]
for seek in seq {
if (seek == c){
if (seek == c) {
return true
}
}
return false
}
public func matchesAnySorted(_ seq : [UnicodeScalar]) -> Bool {
public func matchesAnySorted(_ seq: [UnicodeScalar]) -> Bool {
return !isEmpty() && seq.binarySearch(seq, input[pos]) >= 0
}
public func matchesLetter()-> Bool {
if (isEmpty()){
public func matchesLetter() -> Bool {
if (isEmpty()) {
return false
}
let c = input[pos]
return (c >= "A" && c <= "Z") || (c >= "a" && c <= "z") || c.isMemberOfCharacterSet(CharacterSet.letters)
}
public func matchesDigit()->Bool {
if (isEmpty()){
public func matchesDigit() -> Bool {
if (isEmpty()) {
return false
}
let c = input[pos]
@ -368,7 +360,7 @@ public final class CharacterReader
}
@discardableResult
public func matchConsume(_ seq: String)->Bool {
public func matchConsume(_ seq: String) -> Bool {
if (matches(seq)) {
pos += seq.unicodeScalars.count
return true
@ -378,7 +370,7 @@ public final class CharacterReader
}
@discardableResult
public func matchConsumeIgnoreCase(_ seq: String)->Bool {
public func matchConsumeIgnoreCase(_ seq: String) -> Bool {
if (matchesIgnoreCase(seq)) {
pos += seq.unicodeScalars.count
return true
@ -387,20 +379,18 @@ public final class CharacterReader
}
}
public func containsIgnoreCase(_ seq: String )->Bool {
public func containsIgnoreCase(_ seq: String ) -> Bool {
// used to check presence of </title>, </style>. only finds consistent case.
let loScan = seq.lowercased(with: Locale(identifier: "en"))
let hiScan = seq.uppercased(with: Locale(identifier: "eng"))
return (nextIndexOf(loScan) > -1) || (nextIndexOf(hiScan) > -1)
}
public func toString()->String {
public func toString() -> String {
return String.unicodescalars(Array(input[pos..<length]))
//return input.string(pos, length - pos)
}
/**
* Caches short strings, as a flywheel pattern, to reduce GC load. Just for this doc, to prevent leaks.
* <p />
@ -410,17 +400,17 @@ public final class CharacterReader
*/
private func cacheString(_ start: Int, _ count: Int) -> String {
let val = input
var cache : [String?] = stringCache
var cache: [String?] = stringCache
// limit (no cache):
if (count > CharacterReader.maxCacheLen){
if (count > CharacterReader.maxCacheLen) {
return String.unicodescalars(Array(val[start..<start+count]))
}
// calculate hash:
var hash : Int = 0
var hash: Int = 0
var offset = start
for _ in 0..<count{
for _ in 0..<count {
let ch = val[offset].value
hash = Int.addWithOverflow(Int.multiplyWithOverflow(31, hash).0, Int(ch)).0
offset+=1
@ -429,11 +419,10 @@ public final class CharacterReader
// get from cache
hash = abs(hash)
let i = hash % cache.count
let index : Int = abs(i) //Int(hash & Int(cache.count) - 1)
let index: Int = abs(i) //Int(hash & Int(cache.count) - 1)
var cached = cache[index]
if (cached == nil)
{ // miss, add
if (cached == nil) { // miss, add
cached = String.unicodescalars(Array(val[start..<start+count]))
//cached = val.string(start, count)
cache[Int(index)] = cached
@ -453,16 +442,14 @@ public final class CharacterReader
* Check if the value of the provided range equals the string.
*/
public func rangeEquals(_ start: Int, _ count: Int, _ cached: String) -> Bool {
if (count == cached.unicodeScalars.count)
{
if (count == cached.unicodeScalars.count) {
var count = count
let one = input
var i = start
var j = 0
while (count != 0) {
count -= 1
if (one[i] != cached.unicodeScalar(j) )
{
if (one[i] != cached.unicodeScalar(j) ) {
return false
}
j += 1

View File

@ -8,9 +8,8 @@
import Foundation
open class Cleaner
{
fileprivate let whitelist : Whitelist
open class Cleaner {
fileprivate let whitelist: Whitelist
/**
Create a new cleaner, that sanitizes documents using the supplied whitelist.
@ -56,7 +55,7 @@ open class Cleaner
@discardableResult
fileprivate func copySafeNodes(_ source: Element, _ dest: Element)throws->Int {
let cleaningVisitor: Cleaner.CleaningVisitor = Cleaner.CleaningVisitor(source, dest,self)
let cleaningVisitor: Cleaner.CleaningVisitor = Cleaner.CleaningVisitor(source, dest, self)
let traversor: NodeTraversor = NodeTraversor(cleaningVisitor)
try traversor.traverse(source)
return cleaningVisitor.numDiscarded
@ -68,13 +67,11 @@ open class Cleaner
let dest: Element = try Element(Tag.valueOf(sourceTag), sourceEl.getBaseUri(), destAttrs)
var numDiscarded: Int = 0
if let sourceAttrs = sourceEl.getAttributes()
{
for sourceAttr: Attribute in sourceAttrs
{
if (whitelist.isSafeAttribute(sourceTag, sourceEl, sourceAttr)){
if let sourceAttrs = sourceEl.getAttributes() {
for sourceAttr: Attribute in sourceAttrs {
if (whitelist.isSafeAttribute(sourceTag, sourceEl, sourceAttr)) {
destAttrs.put(attribute: sourceAttr)
}else{
} else {
numDiscarded+=1
}
}
@ -87,18 +84,15 @@ open class Cleaner
}
extension Cleaner
{
fileprivate final class CleaningVisitor : NodeVisitor
{
extension Cleaner {
fileprivate final class CleaningVisitor: NodeVisitor {
var numDiscarded: Int = 0
let root: Element
var destination: Element? // current element to append nodes to
private weak var cleaner : Cleaner?
private weak var cleaner: Cleaner?
public init(_ root: Element, _ destination: Element, _ cleaner : Cleaner) {
public init(_ root: Element, _ destination: Element, _ cleaner: Cleaner) {
self.root = root
self.destination = destination
}
@ -118,11 +112,8 @@ extension Cleaner
} else if let sourceText = (source as? TextNode) {
let destText: TextNode = TextNode(sourceText.getWholeText(), source.getBaseUri())
try destination?.appendChild(destText)
}
else if let sourceData = (source as? DataNode)
{
if sourceData.parent() != nil && cleaner!.whitelist.isSafeTag(sourceData.parent()!.nodeName())
{
} else if let sourceData = (source as? DataNode) {
if sourceData.parent() != nil && cleaner!.whitelist.isSafeTag(sourceData.parent()!.nodeName()) {
//let sourceData: DataNode = (DataNode) source
let destData: DataNode = DataNode(sourceData.getWholeData(), source.getBaseUri())
try destination?.appendChild(destData)
@ -132,12 +123,9 @@ extension Cleaner
}
}
public func tail(_ source: Node, _ depth: Int)throws
{
if let x = (source as? Element)
{
if cleaner!.whitelist.isSafeTag(x.nodeName())
{
public func tail(_ source: Node, _ depth: Int)throws {
if let x = (source as? Element) {
if cleaner!.whitelist.isSafeTag(x.nodeName()) {
// would have descended, so pop destination stack
destination = destination?.parent()
}
@ -146,8 +134,7 @@ extension Cleaner
}
}
extension Cleaner
{
extension Cleaner {
fileprivate struct ElementMeta {
let el: Element
let numAttribsDiscarded: Int

View File

@ -25,14 +25,14 @@ open class Collector {
@return list of matches; empty if none
*/
open static func collect (_ eval: Evaluator, _ root: Element)throws->Elements {
let elements : Elements = Elements()
let elements: Elements = Elements()
try NodeTraversor(Accumulator(root, elements, eval)).traverse(root)
return elements
}
}
private final class Accumulator : NodeVisitor {
private final class Accumulator: NodeVisitor {
private let root: Element
private let elements: Elements
private let eval: Evaluator
@ -44,13 +44,12 @@ private final class Accumulator : NodeVisitor {
}
open func head(_ node: Node, _ depth: Int) {
if let el = node as? Element
{
do{
if (try eval.matches(root, el)){
if let el = node as? Element {
do {
if (try eval.matches(root, el)) {
elements.add(el)
}
}catch{}
} catch {}
}
}

View File

@ -11,10 +11,10 @@ import Foundation
/**
* Base combining (and, or) evaluator.
*/
public class CombiningEvaluator : Evaluator {
public class CombiningEvaluator: Evaluator {
public private(set) var evaluators: Array<Evaluator>
var num : Int = 0
var num: Int = 0
public override init() {
evaluators = Array<Evaluator>()
@ -27,7 +27,7 @@ public class CombiningEvaluator : Evaluator {
updateNumEvaluators()
}
func rightMostEvaluator()->Evaluator? {
func rightMostEvaluator() -> Evaluator? {
return num > 0 && evaluators.count > 0 ? evaluators[num - 1] : nil
}
@ -40,7 +40,7 @@ public class CombiningEvaluator : Evaluator {
num = evaluators.count
}
public final class And : CombiningEvaluator {
public final class And: CombiningEvaluator {
public override init(_ evaluators: Array<Evaluator>) {
super.init(evaluators)
}
@ -49,36 +49,35 @@ public class CombiningEvaluator : Evaluator {
super.init(evaluators)
}
public override func matches(_ root: Element, _ node: Element)->Bool {
for i in 0..<num
{
public override func matches(_ root: Element, _ node: Element) -> Bool {
for i in 0..<num {
let s = evaluators[i]
do{
if (try !s.matches(root, node)){
do {
if (try !s.matches(root, node)) {
return false
}
}catch{}
} catch {}
}
return true
}
public override func toString()->String {
let ar : [String] = evaluators.map { String($0.toString()) }
public override func toString() -> String {
let ar: [String] = evaluators.map { String($0.toString()) }
return StringUtil.join(ar, sep: " ")
}
}
public final class Or : CombiningEvaluator {
public final class Or: CombiningEvaluator {
/**
* Create a new Or evaluator. The initial evaluators are ANDed together and used as the first clause of the OR.
* @param evaluators initial OR clause (these are wrapped into an AND evaluator).
*/
public override init(_ evaluators: Array<Evaluator>) {
super.init()
if (num > 1){
if (num > 1) {
self.evaluators.append(And(evaluators))
}else{ // 0 or 1
} else { // 0 or 1
self.evaluators.append(contentsOf: evaluators)
}
updateNumEvaluators()
@ -86,9 +85,9 @@ public class CombiningEvaluator : Evaluator {
override init(_ evaluators: Evaluator...) {
super.init()
if (num > 1){
if (num > 1) {
self.evaluators.append(And(evaluators))
}else{ // 0 or 1
} else { // 0 or 1
self.evaluators.append(contentsOf: evaluators)
}
updateNumEvaluators()
@ -103,21 +102,20 @@ public class CombiningEvaluator : Evaluator {
updateNumEvaluators()
}
public override func matches(_ root: Element, _ node: Element)->Bool {
for i in 0..<num
{
let s : Evaluator = evaluators[i]
do{
if (try s.matches(root, node)){
public override func matches(_ root: Element, _ node: Element) -> Bool {
for i in 0..<num {
let s: Evaluator = evaluators[i]
do {
if (try s.matches(root, node)) {
return true
}
}catch{}
} catch {}
}
return false
}
public override func toString()->String {
return ":or\(evaluators.map{String($0.toString())})"
public override func toString() -> String {
return ":or\(evaluators.map {String($0.toString())})"
}
}
}

View File

@ -11,8 +11,8 @@ import Foundation
/**
A comment node.
*/
public class Comment : Node {
private static let COMMENT_KEY: String = "comment";
public class Comment: Node {
private static let COMMENT_KEY: String = "comment"
/**
Create a new comment node.
@ -20,59 +20,56 @@ public class Comment : Node {
@param baseUri base URI
*/
public init(_ data: String, _ baseUri: String) {
super.init(baseUri);
do{
try attributes?.put(Comment.COMMENT_KEY, data);
}catch{}
super.init(baseUri)
do {
try attributes?.put(Comment.COMMENT_KEY, data)
} catch {}
}
public override func nodeName()->String {
return "#comment";
public override func nodeName() -> String {
return "#comment"
}
/**
Get the contents of the comment.
@return comment content
*/
public func getData()->String {
return attributes!.get(key: Comment.COMMENT_KEY);
public func getData() -> String {
return attributes!.get(key: Comment.COMMENT_KEY)
}
override func outerHtmlHead(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {
if (out.prettyPrint()){
indent(accum, depth, out);
if (out.prettyPrint()) {
indent(accum, depth, out)
}
accum
.append("<!--")
.append(getData())
.append("-->");
.append("-->")
}
override func outerHtmlTail(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {}
public override func toString()->String {
do{
public override func toString() -> String {
do {
return try
outerHtml();
}catch{
outerHtml()
} catch {
return ""
}
}
public override func copy(with zone: NSZone? = nil) -> Any
{
let clone = Comment(attributes!.get(key: Comment.COMMENT_KEY),baseUri!)
public override func copy(with zone: NSZone? = nil) -> Any {
let clone = Comment(attributes!.get(key: Comment.COMMENT_KEY), baseUri!)
return copy(clone: clone)
}
public override func copy(parent: Node?)->Node
{
let clone = Comment(attributes!.get(key: Comment.COMMENT_KEY),baseUri!)
return copy(clone: clone,parent: parent)
public override func copy(parent: Node?) -> Node {
let clone = Comment(attributes!.get(key: Comment.COMMENT_KEY), baseUri!)
return copy(clone: clone, parent: parent)
}
public override func copy(clone: Node, parent: Node?)->Node
{
return super.copy(clone: clone,parent: parent)
public override func copy(clone: Node, parent: Node?) -> Node {
return super.copy(clone: clone, parent: parent)
}
}

View File

@ -8,6 +8,3 @@
import Foundation
//TODO:

View File

@ -11,8 +11,8 @@ import Foundation
/**
A data node, for contents of style, script tags etc, where contents should not show in text().
*/
open class DataNode : Node{
private static let DATA_KEY : String = "data"
open class DataNode: Node {
private static let DATA_KEY: String = "data"
/**
Create a new DataNode.
@ -21,13 +21,13 @@ open class DataNode : Node{
*/
public init(_ data: String, _ baseUri: String) {
super.init(baseUri)
do{
do {
try attributes?.put(DataNode.DATA_KEY, data)
}catch{}
} catch {}
}
open override func nodeName()->String {
open override func nodeName() -> String {
return "#data"
}
@ -35,7 +35,7 @@ open class DataNode : Node{
Get the data contents of this node. Will be unescaped and with original new lines, space etc.
@return data
*/
open func getWholeData()->String {
open func getWholeData() -> String {
return attributes!.get(key: DataNode.DATA_KEY)
}
@ -45,10 +45,10 @@ open class DataNode : Node{
* @return this node, for chaining
*/
@discardableResult
open func setWholeData(_ data: String)->DataNode {
do{
open func setWholeData(_ data: String) -> DataNode {
do {
try attributes?.put(DataNode.DATA_KEY, data)
}catch{}
} catch {}
return self
}
@ -58,7 +58,6 @@ open class DataNode : Node{
override func outerHtmlTail(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {}
open override func toString()throws->String {
return try outerHtml()
}
@ -74,20 +73,17 @@ open class DataNode : Node{
return DataNode(data, baseUri)
}
public override func copy(with zone: NSZone? = nil) -> Any
{
let clone = DataNode(attributes!.get(key: DataNode.DATA_KEY),baseUri!)
public override func copy(with zone: NSZone? = nil) -> Any {
let clone = DataNode(attributes!.get(key: DataNode.DATA_KEY), baseUri!)
return copy(clone: clone)
}
public override func copy(parent: Node?)->Node
{
let clone = DataNode(attributes!.get(key: DataNode.DATA_KEY),baseUri!)
return copy(clone: clone,parent: parent)
public override func copy(parent: Node?) -> Node {
let clone = DataNode(attributes!.get(key: DataNode.DATA_KEY), baseUri!)
return copy(clone: clone, parent: parent)
}
public override func copy(clone: Node, parent: Node?)->Node
{
return super.copy(clone: clone,parent: parent)
public override func copy(clone: Node, parent: Node?) -> Node {
return super.copy(clone: clone, parent: parent)
}
}

View File

@ -21,5 +21,4 @@ class DataUtil {
static let mimeBoundaryChars = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".characters
static let boundaryLength = 32
}

View File

@ -8,8 +8,7 @@
import Foundation
open class Document : Element
{
open class Document: Element {
public enum QuirksMode {
case noQuirks, quirks, limitedQuirks
}
@ -25,7 +24,7 @@ open class Document : Element
@see org.jsoup.Jsoup#parse
@see #createShell
*/
public init(_ baseUri: String){
public init(_ baseUri: String) {
self._location = baseUri
super.init(try! Tag.valueOf("#root", ParseSettings.htmlDefault), baseUri)
}
@ -35,7 +34,7 @@ open class Document : Element
@param baseUri baseUri of document
@return document with html, head, and body elements.
*/
static open func createShell(_ baseUri: String)->Document {
static open func createShell(_ baseUri: String) -> Document {
let doc: Document = Document(baseUri)
let html: Element = try! doc.appendElement("html")
try! html.appendElement("head")
@ -49,7 +48,7 @@ open class Document : Element
* this will return the final URL from which the document was served from.
* @return location
*/
public func location()->String {
public func location() -> String {
return _location
}
@ -57,7 +56,7 @@ open class Document : Element
Accessor to the document's {@code head} element.
@return {@code head}
*/
public func head()->Element? {
public func head() -> Element? {
return findFirstElementByTagName("head", self)
}
@ -65,7 +64,7 @@ open class Document : Element
Accessor to the document's {@code body} element.
@return {@code body}
*/
public func body()->Element? {
public func body() -> Element? {
return findFirstElementByTagName("body", self)
}
@ -110,15 +109,15 @@ open class Document : Element
@discardableResult
public func normalise()throws->Document {
var htmlE: Element? = findFirstElementByTagName("html", self)
if (htmlE == nil){
if (htmlE == nil) {
htmlE = try appendElement("html")
}
let htmlEl: Element = htmlE!
if (head() == nil){
if (head() == nil) {
try htmlEl.prependElement("head")
}
if (body() == nil){
if (body() == nil) {
try htmlEl.appendElement("body")
}
@ -139,17 +138,15 @@ open class Document : Element
// does not recurse.
private func normaliseTextNodes(_ element: Element)throws {
var toMove: Array<Node> = Array<Node>()
for node:Node in element.childNodes
{
for node: Node in element.childNodes {
if let tn = (node as? TextNode) {
if (!tn.isBlank()){
if (!tn.isBlank()) {
toMove.append(tn)
}
}
}
for i in toMove.count-1...0
{
for i in toMove.count-1...0 {
let node: Node = toMove[i]
try element.removeChild(node)
try body()?.prependChild(TextNode(" ", ""))
@ -162,18 +159,16 @@ open class Document : Element
let elements: Elements = try self.getElementsByTag(tag)
let master: Element? = elements.first() // will always be available as created above if not existent
if (elements.size() > 1) { // dupes, move contents to master
var toMove:Array<Node> = Array<Node>()
for i in 1..<elements.size()
{
var toMove: Array<Node> = Array<Node>()
for i in 1..<elements.size() {
let dupe: Node = elements.get(i)
for node:Node in dupe.childNodes
{
for node: Node in dupe.childNodes {
toMove.append(node)
}
try dupe.remove()
}
for dupe:Node in toMove{
for dupe: Node in toMove {
try master?.appendChild(dupe)
}
}
@ -183,15 +178,14 @@ open class Document : Element
}
}
// fast method to get first by tag name, used for html, head, body finders
private func findFirstElementByTagName(_ tag: String, _ node: Node)->Element? {
if (node.nodeName()==tag){
private func findFirstElementByTagName(_ tag: String, _ node: Node) -> Element? {
if (node.nodeName()==tag) {
return node as? Element
}else {
for child:Node in node.childNodes {
} else {
for child: Node in node.childNodes {
let found: Element? = findFirstElementByTagName(tag, child)
if (found != nil){
if (found != nil) {
return found
}
}
@ -214,7 +208,7 @@ open class Document : Element
return self
}
open override func nodeName()->String {
open override func nodeName() -> String {
return "#document"
}
@ -285,7 +279,7 @@ open class Document : Element
* @return Returns <tt>true</tt> if the element is updated on charset
* changes, <tt>false</tt> if not
*/
public func updateMetaCharsetElement()->Bool {
public func updateMetaCharsetElement() -> Bool {
return updateMetaCharset
}
@ -363,7 +357,7 @@ open class Document : Element
* Get the document's current output settings.
* @return the document's current output settings.
*/
public func outputSettings()->OutputSettings {
public func outputSettings() -> OutputSettings {
return _outputSettings
}
@ -373,7 +367,7 @@ open class Document : Element
* @return this document, for chaining.
*/
@discardableResult
public func outputSettings(_ outputSettings: OutputSettings)->Document {
public func outputSettings(_ outputSettings: OutputSettings) -> Document {
self._outputSettings = outputSettings
return self
}
@ -383,33 +377,29 @@ open class Document : Element
}
@discardableResult
public func quirksMode(_ quirksMode: Document.QuirksMode)->Document {
public func quirksMode(_ quirksMode: Document.QuirksMode) -> Document {
self._quirksMode = quirksMode
return self
}
public override func copy(with zone: NSZone? = nil) -> Any
{
public override func copy(with zone: NSZone? = nil) -> Any {
let clone = Document(_location)
return copy(clone: clone)
}
public override func copy(parent: Node?)->Node
{
public override func copy(parent: Node?) -> Node {
let clone = Document(_location)
return copy(clone: clone,parent: parent)
return copy(clone: clone, parent: parent)
}
public override func copy(clone: Node, parent: Node?)->Node
{
public override func copy(clone: Node, parent: Node?) -> Node {
let clone = clone as! Document
clone._outputSettings = _outputSettings.copy() as! OutputSettings
clone._quirksMode = _quirksMode
clone.updateMetaCharset = updateMetaCharset
return super.copy(clone: clone,parent: parent)
return super.copy(clone: clone, parent: parent)
}
}
public class OutputSettings: NSCopying {
@ -418,11 +408,11 @@ public class OutputSettings: NSCopying {
*/
public enum Syntax {case html, xml}
private var _escapeMode : Entities.EscapeMode = Entities.EscapeMode.base
private var _encoder : String.Encoding = String.Encoding.utf8 // Charset.forName("UTF-8")
private var _prettyPrint : Bool = true
private var _outline : Bool = false
private var _indentAmount : UInt = 1
private var _escapeMode: Entities.EscapeMode = Entities.EscapeMode.base
private var _encoder: String.Encoding = String.Encoding.utf8 // Charset.forName("UTF-8")
private var _prettyPrint: Bool = true
private var _outline: Bool = false
private var _indentAmount: UInt = 1
private var _syntax = Syntax.html
public init() {}
@ -482,13 +472,11 @@ public class OutputSettings: NSCopying {
return encoder(e)
}
/**
* Get the document's current output syntax.
* @return current syntax
*/
public func syntax()-> Syntax {
public func syntax() -> Syntax {
return _syntax
}
@ -499,7 +487,7 @@ public class OutputSettings: NSCopying {
* @return the document's output settings, for chaining
*/
@discardableResult
public func syntax(syntax: Syntax)->OutputSettings {
public func syntax(syntax: Syntax) -> OutputSettings {
_syntax = syntax
return self
}
@ -509,7 +497,7 @@ public class OutputSettings: NSCopying {
* the output, and the output will generally look like the input.
* @return if pretty printing is enabled.
*/
public func prettyPrint()->Bool {
public func prettyPrint() -> Bool {
return _prettyPrint
}
@ -519,7 +507,7 @@ public class OutputSettings: NSCopying {
* @return this, for chaining
*/
@discardableResult
public func prettyPrint(pretty: Bool)->OutputSettings {
public func prettyPrint(pretty: Bool) -> OutputSettings {
_prettyPrint = pretty
return self
}
@ -529,7 +517,7 @@ public class OutputSettings: NSCopying {
* all tags as block.
* @return if outline mode is enabled.
*/
public func outline()->Bool {
public func outline() -> Bool {
return _outline
}
@ -539,7 +527,7 @@ public class OutputSettings: NSCopying {
* @return this, for chaining
*/
@discardableResult
public func outline(outlineMode: Bool)->OutputSettings {
public func outline(outlineMode: Bool) -> OutputSettings {
_outline = outlineMode
return self
}
@ -548,7 +536,7 @@ public class OutputSettings: NSCopying {
* Get the current tag indent amount, used when pretty printing.
* @return the current indent amount
*/
public func indentAmount()-> UInt {
public func indentAmount() -> UInt {
return _indentAmount
}
@ -558,13 +546,12 @@ public class OutputSettings: NSCopying {
* @return this, for chaining
*/
@discardableResult
public func indentAmount(indentAmount: UInt)-> OutputSettings {
public func indentAmount(indentAmount: UInt) -> OutputSettings {
_indentAmount = indentAmount
return self
}
public func copy(with zone: NSZone? = nil) -> Any{
public func copy(with zone: NSZone? = nil) -> Any {
let clone: OutputSettings = OutputSettings()
clone.charset(_encoder) // new charset and charset encoder
clone._escapeMode = _escapeMode//Entities.EscapeMode.valueOf(escapeMode.name())
@ -572,7 +559,4 @@ public class OutputSettings: NSCopying {
return clone
}
}

View File

@ -11,10 +11,10 @@ import Foundation
/**
* A {@code <!DOCTYPE>} node.
*/
public class DocumentType : Node {
private static let NAME: String = "name";
private static let PUBLIC_ID: String = "publicId";
private static let SYSTEM_ID: String = "systemId";
public class DocumentType: Node {
private static let NAME: String = "name"
private static let PUBLIC_ID: String = "publicId"
private static let SYSTEM_ID: String = "systemId"
// todo: quirk mode from publicId and systemId
/**
@ -25,57 +25,56 @@ public class DocumentType : Node {
* @param baseUri the doctype's base URI
*/
public init(_ name: String, _ publicId: String, _ systemId: String, _ baseUri: String) {
super.init(baseUri);
do{
try attr(DocumentType.NAME, name);
try attr(DocumentType.PUBLIC_ID, publicId);
try attr(DocumentType.SYSTEM_ID, systemId);
}catch{}
super.init(baseUri)
do {
try attr(DocumentType.NAME, name)
try attr(DocumentType.PUBLIC_ID, publicId)
try attr(DocumentType.SYSTEM_ID, systemId)
} catch {}
}
public override func nodeName()->String {
return "#doctype";
public override func nodeName() -> String {
return "#doctype"
}
override func outerHtmlHead(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {
if (out.syntax() == OutputSettings.Syntax.html && !has(DocumentType.PUBLIC_ID) && !has(DocumentType.SYSTEM_ID)) {
// looks like a html5 doctype, go lowercase for aesthetics
accum.append("<!doctype");
accum.append("<!doctype")
} else {
accum.append("<!DOCTYPE");
accum.append("<!DOCTYPE")
}
if (has(DocumentType.NAME)){
do{
accum.append(" ").append(try attr(DocumentType.NAME));
}catch{}
if (has(DocumentType.NAME)) {
do {
accum.append(" ").append(try attr(DocumentType.NAME))
} catch {}
}
if (has(DocumentType.PUBLIC_ID)){
do{
accum.append(" PUBLIC \"").append(try attr(DocumentType.PUBLIC_ID)).append("\"");
}catch{}
if (has(DocumentType.PUBLIC_ID)) {
do {
accum.append(" PUBLIC \"").append(try attr(DocumentType.PUBLIC_ID)).append("\"")
} catch {}
}
if (has(DocumentType.SYSTEM_ID)){
do{
accum.append(" \"").append(try attr(DocumentType.SYSTEM_ID)).append("\"");
}catch{}
if (has(DocumentType.SYSTEM_ID)) {
do {
accum.append(" \"").append(try attr(DocumentType.SYSTEM_ID)).append("\"")
} catch {}
}
accum.append(">");
accum.append(">")
}
override func outerHtmlTail(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {
}
private func has(_ attribute: String)->Bool {
do{
return !StringUtil.isBlank(try attr(attribute));
}catch{return false}
private func has(_ attribute: String) -> Bool {
do {
return !StringUtil.isBlank(try attr(attribute))
} catch {return false}
}
public override func copy(with zone: NSZone? = nil) -> Any
{
public override func copy(with zone: NSZone? = nil) -> Any {
let clone = DocumentType(attributes!.get(key: DocumentType.NAME),
attributes!.get(key: DocumentType.PUBLIC_ID),
attributes!.get(key: DocumentType.SYSTEM_ID),
@ -83,18 +82,16 @@ public class DocumentType : Node {
return copy(clone: clone)
}
public override func copy(parent: Node?)->Node
{
public override func copy(parent: Node?) -> Node {
let clone = DocumentType(attributes!.get(key: DocumentType.NAME),
attributes!.get(key: DocumentType.PUBLIC_ID),
attributes!.get(key: DocumentType.SYSTEM_ID),
baseUri!)
return copy(clone: clone,parent: parent)
return copy(clone: clone, parent: parent)
}
public override func copy(clone: Node, parent: Node?)->Node
{
return super.copy(clone: clone,parent: parent)
public override func copy(clone: Node, parent: Node?) -> Node {
return super.copy(clone: clone, parent: parent)
}
}

View File

@ -8,9 +8,8 @@
import Foundation
open class Element : Node
{
var _tag : Tag
open class Element: Node {
var _tag: Tag
//private static let classSplit : Pattern = Pattern("\\s+")
private static let classSplit = "\\s+"
@ -41,7 +40,7 @@ open class Element : Node
super.init(baseUri, Attributes())
}
open override func nodeName()->String {
open override func nodeName() -> String {
return _tag.getName()
}
/**
@ -49,7 +48,7 @@ open class Element : Node
*
* @return the tag name
*/
open func tagName()->String {
open func tagName() -> String {
return _tag.getName()
}
@ -72,7 +71,7 @@ open class Element : Node
*
* @return the tag object
*/
open func tag()->Tag {
open func tag() -> Tag {
return _tag
}
@ -82,7 +81,7 @@ open class Element : Node
*
* @return true if block, false if not (and thus inline)
*/
open func isBlock()->Bool {
open func isBlock() -> Bool {
return _tag.isBlock()
}
@ -91,11 +90,11 @@ open class Element : Node
*
* @return The id attribute, if present, or an empty string if not.
*/
open func id()->String {
open func id() -> String {
guard let attributes = attributes else {return ""}
do{
do {
return try attributes.getIgnoreCase(key: "id")
}catch{}
} catch {}
return ""
}
@ -140,11 +139,11 @@ open class Element : Node
* You can find elements that have data attributes using the {@code [^data-]} attribute key prefix selector.
* @return a map of {@code key=value} custom data attributes.
*/
open func dataset()->Dictionary<String,String> {
open func dataset()->Dictionary<String, String> {
return attributes!.dataset()
}
open override func parent()->Element? {
open override func parent() -> Element? {
return parentNode as? Element
}
@ -152,14 +151,14 @@ open class Element : Node
* Get this element's parent and ancestors, up to the document root.
* @return this element's stack of parents, closest first.
*/
open func parents()->Elements {
open func parents() -> Elements {
let parents: Elements = Elements()
Element.accumulateParents(self, parents)
return parents
}
private static func accumulateParents(_ el: Element, _ parents: Elements) {
let parent : Element? = el.parent()
let parent: Element? = el.parent()
if (parent != nil && !(parent!.tagName() == "#root")) {
parents.add(parent!)
accumulateParents(parent!, parents)
@ -177,7 +176,7 @@ open class Element : Node
* @return the child element, if it exists, otherwise throws an {@code IndexOutOfBoundsException}
* @see #childNode(int)
*/
open func child(_ index: Int)->Element {
open func child(_ index: Int) -> Element {
return children().get(index)
}
@ -190,13 +189,11 @@ open class Element : Node
* empty list.
* @see #childNodes()
*/
open func children()->Elements {
open func children() -> Elements {
// create on the fly rather than maintaining two lists. if gets slow, memoize, and mark dirty on change
var elements = Array<Element>()
for node in childNodes
{
if let n = node as? Element
{
for node in childNodes {
if let n = node as? Element {
elements.append(n)
}
}
@ -222,7 +219,7 @@ open class Element : Node
open func textNodes()->Array<TextNode> {
var textNodes = Array<TextNode>()
for node in childNodes {
if let n = node as? TextNode{
if let n = node as? TextNode {
textNodes.append(n)
}
}
@ -240,9 +237,8 @@ open class Element : Node
*/
open func dataNodes()->Array<DataNode> {
var dataNodes = Array<DataNode>()
for node in childNodes
{
if let n = node as? DataNode{
for node in childNodes {
if let n = node as? DataNode {
dataNodes.append(n)
}
}
@ -315,7 +311,7 @@ open class Element : Node
//Validate.notNull(children, "Children collection to be inserted must not be null.")
var index = index
let currentSize: Int = childNodeSize()
if (index < 0){ index += currentSize + 1} // roll around
if (index < 0) { index += currentSize + 1} // roll around
try Validate.isTrue(val: index >= 0 && index <= currentSize, msg: "Insert position out of bounds.")
try addChildren(index, children)
@ -358,7 +354,7 @@ open class Element : Node
*/
@discardableResult
public func appendText(_ text: String)throws->Element {
let node:TextNode = TextNode(text, getBaseUri())
let node: TextNode = TextNode(text, getBaseUri())
try appendChild(node)
return self
}
@ -398,7 +394,7 @@ open class Element : Node
@discardableResult
public func prepend(_ html: String)throws->Element {
let nodes: Array<Node> = try Parser.parseFragment(html, self, getBaseUri())
try addChildren(0,nodes)
try addChildren(0, nodes)
return self
}
@ -452,7 +448,7 @@ open class Element : Node
* @return this element
*/
@discardableResult
public func empty()->Element {
public func empty() -> Element {
childNodes.removeAll()
return self
}
@ -479,7 +475,7 @@ open class Element : Node
* @return the CSS Path that can be used to retrieve the element in a selector.
*/
public func cssSelector()throws->String {
if (id().characters.count > 0){
if (id().characters.count > 0) {
return "#" + id()
}
@ -488,7 +484,7 @@ open class Element : Node
let selector: StringBuilder = StringBuilder(string: tagName)
let cl = try classNames()
let classes: String = cl.joined(separator: ".")
if (classes.characters.count > 0){
if (classes.characters.count > 0) {
selector.append(".").append(classes)
}
@ -498,7 +494,7 @@ open class Element : Node
}
selector.insert(0, " > ")
if (try parent()!.select(selector.toString()).array().count > 1){
if (try parent()!.select(selector.toString()).array().count > 1) {
selector.append(":nth-child(\(try elementSiblingIndex() + 1))")
}
@ -510,14 +506,14 @@ open class Element : Node
* of itself, so will not be included in the returned list.
* @return sibling elements
*/
public func siblingElements()->Elements {
if (parentNode == nil){return Elements()}
public func siblingElements() -> Elements {
if (parentNode == nil) {return Elements()}
let elements: Array<Element>? = parent()?.children().array()
let siblings: Elements = Elements()
if let elements = elements{
for el:Element in elements{
if (el != self){
if let elements = elements {
for el: Element in elements {
if (el != self) {
siblings.add(el)
}
}
@ -539,10 +535,10 @@ open class Element : Node
let siblings: Array<Element>? = parent()?.children().array()
let index: Int? = try Element.indexInList(self, siblings)
try Validate.notNull(obj: index)
if let siblings = siblings{
if (siblings.count > index!+1){
if let siblings = siblings {
if (siblings.count > index!+1) {
return siblings[index!+1]
}else{
} else {
return nil}
}
return nil
@ -558,9 +554,9 @@ open class Element : Node
let siblings: Array<Element>? = parent()?.children().array()
let index: Int? = try Element.indexInList(self, siblings)
try Validate.notNull(obj: index)
if (index! > 0){
if (index! > 0) {
return siblings?[index!-1]
}else{
} else {
return nil
}
}
@ -569,7 +565,7 @@ open class Element : Node
* Gets the first element sibling of this element.
* @return the first sibling that is an element (aka the parent's first element child)
*/
public func firstElementSibling()->Element? {
public func firstElementSibling() -> Element? {
// todo: should firstSibling() exclude this?
let siblings: Array<Element>? = parent()?.children().array()
return (siblings != nil && siblings!.count > 1) ? siblings![0] : nil
@ -590,18 +586,17 @@ open class Element : Node
* Gets the last element sibling of this element
* @return the last sibling that is an element (aka the parent's last element child)
*/
public func lastElementSibling()->Element? {
public func lastElementSibling() -> Element? {
let siblings: Array<Element>? = parent()?.children().array()
return (siblings != nil && siblings!.count > 1) ? siblings![siblings!.count - 1] : nil
}
private static func indexInList(_ search: Element, _ elements: Array<Element>?)throws->Int? {
try Validate.notNull(obj: elements)
if let elements = elements
{
if let elements = elements {
for i in 0..<elements.count {
let element: Element = elements[i]
if (element == search){
if (element == search) {
return i
}
}
@ -636,9 +631,9 @@ open class Element : Node
try Validate.notEmpty(string: id)
let elements: Elements = try Collector.collect(Evaluator.Id(id), self)
if (elements.array().count > 0){
if (elements.array().count > 0) {
return elements.get(0)
}else{
} else {
return nil
}
}
@ -660,7 +655,6 @@ open class Element : Node
return try Collector.collect(Evaluator.Class(className), self)
}
/**
* Find elements that have a named attribute set. Case insensitive.
*
@ -760,11 +754,11 @@ open class Element : Node
* @return elements that have attributes matching this regular expression
*/
public func getElementsByAttributeValueMatching(_ key: String, _ regex: String)throws->Elements {
var pattern : Pattern
var pattern: Pattern
do {
pattern = Pattern.compile(regex)
try pattern.validate()
} catch{
} catch {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Pattern syntax error: \(regex)")
}
return try getElementsByAttributeValueMatching(key, pattern)
@ -840,13 +834,12 @@ open class Element : Node
do {
pattern = Pattern.compile(regex)
try pattern.validate()
} catch{
} catch {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Pattern syntax error: \(regex)")
}
return try getElementsMatchingText(pattern)
}
/**
* Find elements whose own text matches the supplied regular expression.
* @param pattern regular expression to match text against
@ -868,7 +861,7 @@ open class Element : Node
do {
pattern = Pattern.compile(regex)
try pattern.validate()
} catch{
} catch {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Pattern syntax error: \(regex)")
}
return try getElementsMatchingOwnText(pattern)
@ -883,7 +876,6 @@ open class Element : Node
return try Collector.collect(Evaluator.AllElements(), self)
}
/**
* Gets the combined text of this element and all its children. Whitespace is normalized and trimmed.
* <p>
@ -893,20 +885,18 @@ open class Element : Node
* @see #ownText()
* @see #textNodes()
*/
class textNodeVisitor: NodeVisitor
{
class textNodeVisitor: NodeVisitor {
let accum: StringBuilder
init(_ accum: StringBuilder) {
self.accum = accum
}
public func head(_ node: Node, _ depth: Int) {
if let textNode = (node as? TextNode)
{
if let textNode = (node as? TextNode) {
Element.appendNormalisedText(accum, textNode)
} else if let element = (node as? Element) {
if (accum.length > 0 &&
(element.isBlock() || element._tag.getName() == "br") &&
!TextNode.lastCharIsWhitespace(accum)){
!TextNode.lastCharIsWhitespace(accum)) {
accum.append(" ")
}
}
@ -932,7 +922,7 @@ open class Element : Node
* @see #text()
* @see #textNodes()
*/
public func ownText()->String {
public func ownText() -> String {
let sb: StringBuilder = StringBuilder()
ownText(sb)
return sb.toString().trim()
@ -951,20 +941,20 @@ open class Element : Node
private static func appendNormalisedText(_ accum: StringBuilder, _ textNode: TextNode) {
let text: String = textNode.getWholeText()
if (Element.preserveWhitespace(textNode.parentNode)){
if (Element.preserveWhitespace(textNode.parentNode)) {
accum.append(text)
}else{
} else {
StringUtil.appendNormalisedWhitespace(accum, string: text, stripLeading: TextNode.lastCharIsWhitespace(accum))
}
}
private static func appendWhitespaceIfBr(_ element: Element, _ accum: StringBuilder) {
if (element._tag.getName() == "br" && !TextNode.lastCharIsWhitespace(accum)){
if (element._tag.getName() == "br" && !TextNode.lastCharIsWhitespace(accum)) {
accum.append(" ")
}
}
static func preserveWhitespace(_ node: Node?)->Bool {
static func preserveWhitespace(_ node: Node?) -> Bool {
// looks only at this element and one level up, to prevent recursion & needless stack searches
if let element = (node as? Element) {
return element._tag.preserveWhitespace() || element.parent() != nil && element.parent()!._tag.preserveWhitespace()
@ -972,7 +962,6 @@ open class Element : Node
return false
}
/**
* Set the text of this element. Any existing contents (text or elements) will be cleared
* @param text unencoded text
@ -990,14 +979,14 @@ open class Element : Node
Test if this element has any text content (that is not just whitespace).
@return true if element has non-blank text content.
*/
public func hasText()->Bool {
for child:Node in childNodes {
public func hasText() -> Bool {
for child: Node in childNodes {
if let textNode = (child as? TextNode) {
if (!textNode.isBlank()){
if (!textNode.isBlank()) {
return true
}
} else if let el = (child as? Element) {
if (el.hasText()){
if (el.hasText()) {
return true
}
}
@ -1011,7 +1000,7 @@ open class Element : Node
*
* @see #dataNodes()
*/
public func data()->String {
public func data() -> String {
let sb: StringBuilder = StringBuilder()
for childNode: Node in childNodes {
@ -1042,7 +1031,7 @@ open class Element : Node
*/
public func classNames()throws->OrderedSet<String> {
let fitted = try className().replaceAll(of: Element.classSplit, with: " ", options:.caseInsensitive)
let names:[String] = fitted.components(separatedBy: " ")
let names: [String] = fitted.components(separatedBy: " ")
let classNames: OrderedSet<String> = OrderedSet(sequence:names)
classNames.remove("") // if classNames() was empty, would include an empty class
return classNames
@ -1065,7 +1054,7 @@ open class Element : Node
* @return true if it does, false if not
*/
// performance sensitive
public func hasClass(_ className: String)->Bool {
public func hasClass(_ className: String) -> Bool {
let classAtt: String? = attributes?.get(key: "class")
let len: Int = (classAtt != nil) ? classAtt!.characters.count : 0
let wantLen: Int = className.characters.count
@ -1143,8 +1132,8 @@ open class Element : Node
@discardableResult
public func toggleClass(_ className: String)throws->Element {
let classes: OrderedSet<String> = try classNames()
if (classes.contains(className)){classes.remove(className)
}else{
if (classes.contains(className)) {classes.remove(className)
} else {
classes.append(className)
}
try classNames(classes)
@ -1157,9 +1146,9 @@ open class Element : Node
* @return the value of the form element, or empty string if not set.
*/
public func val()throws->String {
if (tagName()=="textarea"){
if (tagName()=="textarea") {
return try text()
}else{
} else {
return try attr("value")
}
}
@ -1170,19 +1159,17 @@ open class Element : Node
* @return this element (for chaining)
*/
@discardableResult
public func val(_ value:String)throws->Element {
if (tagName() == "textarea"){
public func val(_ value: String)throws->Element {
if (tagName() == "textarea") {
try text(value)
}else{
} else {
try attr("value", value)
}
return self
}
override func outerHtmlHead(_ accum:StringBuilder, _ depth: Int, _ out: OutputSettings)throws
{
if (out.prettyPrint() && (_tag.formatAsBlock() || (parent() != nil && parent()!.tag().formatAsBlock()) || out.outline()))
{
override func outerHtmlHead(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings)throws {
if (out.prettyPrint() && (_tag.formatAsBlock() || (parent() != nil && parent()!.tag().formatAsBlock()) || out.outline())) {
if (accum.length > 0) {
indent(accum, depth, out)
}
@ -1194,22 +1181,21 @@ open class Element : Node
// selfclosing includes unknown tags, isEmpty defines tags that are always empty
if (childNodes.isEmpty && _tag.isSelfClosing()) {
if (out.syntax() == OutputSettings.Syntax.html && _tag.isEmpty()){
if (out.syntax() == OutputSettings.Syntax.html && _tag.isEmpty()) {
accum.append(">")
}else{
} else {
accum.append(" />") // <img> in html, <img /> in xml
}
}
else{
} else {
accum.append(">")
}
}
override func outerHtmlTail(_ accum:StringBuilder, _ depth: Int, _ out: OutputSettings){
override func outerHtmlTail(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {
if (!(childNodes.isEmpty && _tag.isSelfClosing())) {
if (out.prettyPrint() && (!childNodes.isEmpty && (
_tag.formatAsBlock() || (out.outline() && (childNodes.count>1 || (childNodes.count==1 && !(((childNodes[0] as? TextNode) != nil)))))
))){
))) {
indent(accum, depth, out)
}
accum.append("</").append(tagName()).append(">")
@ -1230,7 +1216,7 @@ open class Element : Node
}
private func html2(_ accum: StringBuilder)throws {
for node in childNodes{
for node in childNodes {
try node.outerHtml(accum)
}
}
@ -1239,7 +1225,7 @@ open class Element : Node
* {@inheritDoc}
*/
open override func html(_ appendable: StringBuilder)throws->StringBuilder {
for node in childNodes{
for node in childNodes {
try node.outerHtml(appendable)
}
return appendable
@ -1258,32 +1244,26 @@ open class Element : Node
return self
}
open override func toString()throws->String {
return try outerHtml()
}
public override func copy(with zone: NSZone? = nil) -> Any
{
let clone = Element(_tag,baseUri!,attributes!)
public override func copy(with zone: NSZone? = nil) -> Any {
let clone = Element(_tag, baseUri!, attributes!)
return copy(clone: clone)
}
public override func copy(parent: Node?)->Node
{
let clone = Element(_tag,baseUri!,attributes!)
return copy(clone: clone,parent: parent)
public override func copy(parent: Node?) -> Node {
let clone = Element(_tag, baseUri!, attributes!)
return copy(clone: clone, parent: parent)
}
public override func copy(clone: Node, parent: Node?)->Node
{
return super.copy(clone: clone,parent: parent)
public override func copy(clone: Node, parent: Node?) -> Node {
return super.copy(clone: clone, parent: parent)
}
override public var hashValue: Int
{
override public var hashValue: Int {
var h = super.hashValue
h = Int.addWithOverflow(Int.multiplyWithOverflow(31, h).0,_tag.hashValue).0
h = Int.addWithOverflow(Int.multiplyWithOverflow(31, h).0, _tag.hashValue).0
return h
}

View File

@ -16,16 +16,15 @@ import Foundation
//open typealias Elements = Array<Element>
//typealias E = Element
open class Elements: NSCopying
{
fileprivate var this : Array<Element> = Array<Element>()
open class Elements: NSCopying {
fileprivate var this: Array<Element> = Array<Element>()
public init() {
}
public init(_ a: Array<Element>){
public init(_ a: Array<Element>) {
this = a
}
public init(_ a: OrderedSet<Element>){
public init(_ a: OrderedSet<Element>) {
this.append(contentsOf: a)
}
@ -33,13 +32,12 @@ open class Elements: NSCopying
* Creates a deep copy of these elements.
* @return a deep copy
*/
public func copy(with zone: NSZone? = nil) -> Any
{
public func copy(with zone: NSZone? = nil) -> Any {
let clone: Elements = Elements()
for e: Element in this{
for e: Element in this {
clone.add(e.copy() as! Element)
}
return clone;
return clone
}
// attribute methods
@ -52,11 +50,11 @@ open class Elements: NSCopying
*/
open func attr(_ attributeKey: String)throws->String {
for element in this {
if (element.hasAttr(attributeKey)){
if (element.hasAttr(attributeKey)) {
return try element.attr(attributeKey)
}
}
return "";
return ""
}
/**
@ -64,11 +62,11 @@ open class Elements: NSCopying
@param attributeKey attribute key
@return true if any of the elements have the attribute; false if none do.
*/
open func hasAttr(_ attributeKey: String)->Bool {
open func hasAttr(_ attributeKey: String) -> Bool {
for element in this {
if element.hasAttr(attributeKey) {return true}
}
return false;
return false
}
/**
@ -80,9 +78,9 @@ open class Elements: NSCopying
@discardableResult
open func attr(_ attributeKey: String, _ attributeValue: String)throws->Elements {
for element in this {
try element.attr(attributeKey, attributeValue);
try element.attr(attributeKey, attributeValue)
}
return self;
return self
}
/**
@ -93,9 +91,9 @@ open class Elements: NSCopying
@discardableResult
open func removeAttr(_ attributeKey: String)throws->Elements {
for element in this {
try element.removeAttr(attributeKey);
try element.removeAttr(attributeKey)
}
return self;
return self
}
/**
@ -106,9 +104,9 @@ open class Elements: NSCopying
@discardableResult
open func addClass(_ className: String)throws->Elements {
for element in this {
try element.addClass(className);
try element.addClass(className)
}
return self;
return self
}
/**
@ -119,9 +117,9 @@ open class Elements: NSCopying
@discardableResult
open func removeClass(_ className: String)throws->Elements {
for element: Element in this {
try element.removeClass(className);
try element.removeClass(className)
}
return self;
return self
}
/**
@ -132,9 +130,9 @@ open class Elements: NSCopying
@discardableResult
open func toggleClass(_ className: String)throws->Elements {
for element: Element in this {
try element.toggleClass(className);
try element.toggleClass(className)
}
return self;
return self
}
/**
@ -143,13 +141,13 @@ open class Elements: NSCopying
@return true if any do, false if none do
*/
open func hasClass(_ className: String)->Bool {
open func hasClass(_ className: String) -> Bool {
for element: Element in this {
if (element.hasClass(className)){
return true;
if (element.hasClass(className)) {
return true
}
}
return false;
return false
}
/**
@ -158,10 +156,10 @@ open class Elements: NSCopying
* @see Element#val()
*/
open func val()throws->String {
if (size() > 0){
return try first()!.val();
if (size() > 0) {
return try first()!.val()
}
return "";
return ""
}
/**
@ -171,10 +169,10 @@ open class Elements: NSCopying
*/
@discardableResult
open func val(_ value: String)throws->Elements {
for element: Element in this{
try element.val(value);
for element: Element in this {
try element.val(value)
}
return self;
return self
}
/**
@ -186,25 +184,23 @@ open class Elements: NSCopying
* @see Element#text()
*/
open func text()throws->String {
let sb: StringBuilder = StringBuilder();
for element: Element in this
{
if (sb.length != 0){
let sb: StringBuilder = StringBuilder()
for element: Element in this {
if (sb.length != 0) {
sb.append(" ")
}
sb.append(try element.text());
sb.append(try element.text())
}
return sb.toString();
return sb.toString()
}
open func hasText()->Bool {
for element:Element in this
{
if (element.hasText()){
return true;
open func hasText() -> Bool {
for element: Element in this {
if (element.hasText()) {
return true
}
}
return false;
return false
}
/**
@ -214,14 +210,14 @@ open class Elements: NSCopying
* @see #outerHtml()
*/
open func html()throws->String {
let sb: StringBuilder = StringBuilder();
let sb: StringBuilder = StringBuilder()
for element: Element in this {
if (sb.length != 0){
if (sb.length != 0) {
sb.append("\n")
}
sb.append(try element.html());
sb.append(try element.html())
}
return sb.toString();
return sb.toString()
}
/**
@ -231,15 +227,14 @@ open class Elements: NSCopying
* @see #html()
*/
open func outerHtml()throws->String {
let sb: StringBuilder = StringBuilder();
for element in this
{
if (sb.length != 0){
let sb: StringBuilder = StringBuilder()
for element in this {
if (sb.length != 0) {
sb.append("\n")
}
sb.append(try element.outerHtml());
sb.append(try element.outerHtml())
}
return sb.toString();
return sb.toString()
}
/**
@ -250,7 +245,7 @@ open class Elements: NSCopying
*/
open func toString()throws->String {
return try outerHtml();
return try outerHtml()
}
/**
@ -263,9 +258,9 @@ open class Elements: NSCopying
@discardableResult
open func tagName(_ tagName: String)throws->Elements {
for element: Element in this {
try element.tagName(tagName);
try element.tagName(tagName)
}
return self;
return self
}
/**
@ -277,9 +272,9 @@ open class Elements: NSCopying
@discardableResult
open func html(_ html: String)throws->Elements {
for element: Element in this {
try element.html(html);
try element.html(html)
}
return self;
return self
}
/**
@ -291,9 +286,9 @@ open class Elements: NSCopying
@discardableResult
open func prepend(_ html: String)throws->Elements {
for element: Element in this {
try element.prepend(html);
try element.prepend(html)
}
return self;
return self
}
/**
@ -305,9 +300,9 @@ open class Elements: NSCopying
@discardableResult
open func append(_ html: String)throws->Elements {
for element: Element in this {
try element.append(html);
try element.append(html)
}
return self;
return self
}
/**
@ -319,9 +314,9 @@ open class Elements: NSCopying
@discardableResult
open func before(_ html: String)throws->Elements {
for element: Element in this {
try element.before(html);
try element.before(html)
}
return self;
return self
}
/**
@ -333,9 +328,9 @@ open class Elements: NSCopying
@discardableResult
open func after(_ html: String)throws->Elements {
for element: Element in this {
try element.after(html);
try element.after(html)
}
return self;
return self
}
/**
@ -349,11 +344,11 @@ open class Elements: NSCopying
*/
@discardableResult
open func wrap(_ html: String)throws->Elements {
try Validate.notEmpty(string: html);
try Validate.notEmpty(string: html)
for element: Element in this {
try element.wrap(html);
try element.wrap(html)
}
return self;
return self
}
/**
@ -373,9 +368,9 @@ open class Elements: NSCopying
@discardableResult
open func unwrap()throws->Elements {
for element: Element in this {
try element.unwrap();
try element.unwrap()
}
return self;
return self
}
/**
@ -390,11 +385,11 @@ open class Elements: NSCopying
* @see #remove()
*/
@discardableResult
open func empty()->Elements {
open func empty() -> Elements {
for element: Element in this {
element.empty();
element.empty()
}
return self;
return self
}
/**
@ -411,11 +406,10 @@ open class Elements: NSCopying
*/
@discardableResult
open func remove()throws->Elements {
for element in this
{
try element.remove();
for element in this {
try element.remove()
}
return self;
return self
}
// filters
@ -426,7 +420,7 @@ open class Elements: NSCopying
* @return the filtered list of elements, or an empty list if none match.
*/
open func select(_ query: String)throws->Elements {
return try Selector.select(query, this);
return try Selector.select(query, this)
}
/**
@ -440,8 +434,8 @@ open class Elements: NSCopying
* @return a new elements list that contains only the filtered results
*/
open func not(_ query: String)throws->Elements {
let out: Elements = try Selector.select(query, this);
return Selector.filterOut(this, out.this);
let out: Elements = try Selector.select(query, this)
return Selector.filterOut(this, out.this)
}
/**
@ -451,8 +445,8 @@ open class Elements: NSCopying
* @param index the (zero-based) index of the element in the list to retain
* @return Elements containing only the specified element, or, if that element did not exist, an empty list.
*/
open func eq(_ index: Int)->Elements {
return size() > index ? Elements([get(index)]) : Elements();
open func eq(_ index: Int) -> Elements {
return size() > index ? Elements([get(index)]) : Elements()
}
/**
@ -461,8 +455,8 @@ open class Elements: NSCopying
* @return true if at least one element in the list matches the query.
*/
open func `is`(_ query: String)throws->Bool {
let children: Elements = try select(query);
return !children.isEmpty();
let children: Elements = try select(query)
return !children.isEmpty()
}
/**
@ -470,13 +464,12 @@ open class Elements: NSCopying
* @return all of the parents and ancestor elements of the matched elements
*/
open func parents()->Elements {
let combo: OrderedSet<Element> = OrderedSet<Element>();
for e:Element in this
{
combo.append(contentsOf: e.parents().array());
open func parents() -> Elements {
let combo: OrderedSet<Element> = OrderedSet<Element>()
for e: Element in this {
combo.append(contentsOf: e.parents().array())
}
return Elements(combo);
return Elements(combo)
}
// list-like methods
@ -484,25 +477,24 @@ open class Elements: NSCopying
Get the first matched element.
@return The first matched element, or <code>null</code> if contents is empty.
*/
open func first()->Element? {
return isEmpty() ? nil : get(0);
open func first() -> Element? {
return isEmpty() ? nil : get(0)
}
open func isEmpty()->Bool{
open func isEmpty() -> Bool {
return array().count == 0
}
open func size()->Int{
open func size() -> Int {
return array().count
}
/**
Get the last matched element.
@return The last matched element, or <code>null</code> if contents is empty.
*/
open func last()->Element? {
return isEmpty() ? nil : get(size() - 1);
open func last() -> Element? {
return isEmpty() ? nil : get(size() - 1)
}
/**
@ -513,10 +505,10 @@ open class Elements: NSCopying
@discardableResult
open func traverse(_ nodeVisitor: NodeVisitor)throws->Elements {
let traversor: NodeTraversor = NodeTraversor(nodeVisitor)
for el:Element in this {
try traversor.traverse(el);
for el: Element in this {
try traversor.traverse(el)
}
return self;
return self
}
/**
@ -525,15 +517,13 @@ open class Elements: NSCopying
* no forms.
*/
open func forms()->Array<FormElement> {
var forms: Array<FormElement> = Array<FormElement>();
for el:Element in this
{
if let el = el as? FormElement
{
var forms: Array<FormElement> = Array<FormElement>()
for el: Element in this {
if let el = el as? FormElement {
forms.append(el)
}
}
return forms;
return forms
}
/**
@ -550,18 +540,16 @@ open class Elements: NSCopying
this.insert(element, at: index)
}
open func get(_ i :Int)->Element{
open func get(_ i: Int) -> Element {
return this[i]
}
open func array()->Array<Element>{
open func array()->Array<Element> {
return this
}
}
extension Elements: Equatable
{
extension Elements: Equatable {
/// Returns a Boolean value indicating whether two values are equal.
///
/// Equality is the inverse of inequality. For any values `a` and `b`,
@ -570,9 +558,7 @@ extension Elements: Equatable
/// - Parameters:
/// - lhs: A value to compare.
/// - rhs: Another value to compare.
public static func ==(lhs: Elements, rhs: Elements) -> Bool
{
public static func ==(lhs: Elements, rhs: Elements) -> Bool {
return lhs.this == rhs.this
}
}

File diff suppressed because one or more lines are too long

View File

@ -22,43 +22,38 @@ public class Evaluator {
* @return Returns <tt>true</tt> if the requirements are met or
* <tt>false</tt> otherwise
*/
open func matches(_ root: Element, _ element: Element)throws->Bool{
open func matches(_ root: Element, _ element: Element)throws->Bool {
preconditionFailure("self method must be overridden")
}
open func toString()->String
{
open func toString() -> String {
preconditionFailure("self method must be overridden")
}
/**
* Evaluator for tag name
*/
public class Tag : Evaluator {
private let tagName : String
public class Tag: Evaluator {
private let tagName: String
public init(_ tagName: String) {
self.tagName = tagName
}
open override func matches(_ root: Element, _ element: Element)throws->Bool {
return (element.tagName().equalsIgnoreCase(string: tagName))
}
open override func toString()->String {
open override func toString() -> String {
return String(tagName)
}
}
/**
* Evaluator for tag name that ends with
*/
public final class TagEndsWith : Evaluator {
private let tagName : String
public final class TagEndsWith: Evaluator {
private let tagName: String
public init(_ tagName: String) {
self.tagName = tagName
@ -68,7 +63,7 @@ public class Evaluator {
return (element.tagName().hasSuffix(tagName))
}
open override func toString()->String {
open override func toString() -> String {
return String(tagName)
}
}
@ -76,8 +71,8 @@ public class Evaluator {
/**
* Evaluator for element id
*/
public final class Id : Evaluator {
private let id : String
public final class Id: Evaluator {
private let id: String
public init(_ id: String) {
self.id = id
@ -87,7 +82,7 @@ public class Evaluator {
return (id == element.id())
}
open override func toString()->String {
open override func toString() -> String {
return "#\(id)"
}
@ -96,18 +91,18 @@ public class Evaluator {
/**
* Evaluator for element class
*/
public final class Class : Evaluator {
private let className : String
public final class Class: Evaluator {
private let className: String
public init(_ className: String) {
self.className = className
}
open override func matches(_ root: Element, _ element: Element)->Bool {
open override func matches(_ root: Element, _ element: Element) -> Bool {
return (element.hasClass(className))
}
open override func toString()->String {
open override func toString() -> String {
return ".\(className)"
}
@ -116,8 +111,8 @@ public class Evaluator {
/**
* Evaluator for attribute name matching
*/
public final class Attribute : Evaluator {
private let key : String
public final class Attribute: Evaluator {
private let key: String
public init(_ key: String) {
self.key = key
@ -127,7 +122,7 @@ public class Evaluator {
return element.hasAttr(key)
}
open override func toString()->String {
open override func toString() -> String {
return "[\(key)]"
}
@ -136,8 +131,8 @@ public class Evaluator {
/**
* Evaluator for attribute name prefix matching
*/
public final class AttributeStarting : Evaluator {
private let keyPrefix : String
public final class AttributeStarting: Evaluator {
private let keyPrefix: String
public init(_ keyPrefix: String)throws {
try Validate.notEmpty(string: keyPrefix)
@ -145,9 +140,9 @@ public class Evaluator {
}
open override func matches(_ root: Element, _ element: Element)throws->Bool {
if let values = element.getAttributes(){
if let values = element.getAttributes() {
for attribute in values.iterator() {
if (attribute.getKey().lowercased().hasPrefix(keyPrefix)){
if (attribute.getKey().lowercased().hasPrefix(keyPrefix)) {
return true
}
}
@ -155,7 +150,7 @@ public class Evaluator {
return false
}
open override func toString()->String {
open override func toString() -> String {
return "[^\(keyPrefix)]"
}
@ -164,22 +159,20 @@ public class Evaluator {
/**
* Evaluator for attribute name/value matching
*/
public final class AttributeWithValue : AttributeKeyPair {
public final class AttributeWithValue: AttributeKeyPair {
public override init(_ key: String, _ value: String)throws {
try super.init(key, value)
}
open override func matches(_ root: Element, _ element: Element)throws->Bool
{
if(element.hasAttr(key)){
open override func matches(_ root: Element, _ element: Element)throws->Bool {
if(element.hasAttr(key)) {
let s = try element.attr(key)
return value.equalsIgnoreCase(string: s.trim())
}
return false
}
open override func toString()->String {
open override func toString() -> String {
return "[\(key)=\(value)]"
}
@ -188,7 +181,7 @@ public class Evaluator {
/**
* Evaluator for attribute name != value matching
*/
public final class AttributeWithValueNot : AttributeKeyPair {
public final class AttributeWithValueNot: AttributeKeyPair {
public override init(_ key: String, _ value: String)throws {
try super.init(key, value)
}
@ -198,7 +191,7 @@ public class Evaluator {
return !value.equalsIgnoreCase(string: s)
}
open override func toString()->String {
open override func toString() -> String {
return "[\(key)!=\(value)]"
}
@ -207,19 +200,19 @@ public class Evaluator {
/**
* Evaluator for attribute name/value matching (value prefix)
*/
public final class AttributeWithValueStarting : AttributeKeyPair {
public final class AttributeWithValueStarting: AttributeKeyPair {
public override init(_ key: String, _ value: String)throws {
try super.init(key, value)
}
open override func matches(_ root: Element, _ element: Element)throws->Bool {
if(element.hasAttr(key)){
if(element.hasAttr(key)) {
return try element.attr(key).lowercased().hasPrefix(value) // value is lower case already
}
return false
}
open override func toString()->String {
open override func toString() -> String {
return "[\(key)^=\(value)]"
}
@ -228,19 +221,19 @@ public class Evaluator {
/**
* Evaluator for attribute name/value matching (value ending)
*/
public final class AttributeWithValueEnding : AttributeKeyPair {
public final class AttributeWithValueEnding: AttributeKeyPair {
public override init(_ key: String, _ value: String)throws {
try super.init(key, value)
}
open override func matches(_ root: Element, _ element: Element)throws->Bool {
if(element.hasAttr(key)){
if(element.hasAttr(key)) {
return try element.attr(key).lowercased().hasSuffix(value) // value is lower case
}
return false
}
open override func toString()->String {
open override func toString() -> String {
return "[\(key)$=\(value)]"
}
@ -249,19 +242,19 @@ public class Evaluator {
/**
* Evaluator for attribute name/value matching (value containing)
*/
public final class AttributeWithValueContaining : AttributeKeyPair {
public final class AttributeWithValueContaining: AttributeKeyPair {
public override init(_ key: String, _ value: String)throws {
try super.init(key, value)
}
open override func matches(_ root: Element, _ element: Element)throws->Bool {
if(element.hasAttr(key)){
if(element.hasAttr(key)) {
return try element.attr(key).lowercased().contains(value) // value is lower case
}
return false
}
open override func toString()->String {
open override func toString() -> String {
return "[\(key)*=\(value)]"
}
@ -270,9 +263,9 @@ public class Evaluator {
/**
* Evaluator for attribute name/value matching (value regex matching)
*/
public final class AttributeWithValueMatching : Evaluator {
let key : String
let pattern : Pattern
public final class AttributeWithValueMatching: Evaluator {
let key: String
let pattern: Pattern
public init(_ key: String, _ pattern: Pattern) {
self.key = key.trim().lowercased()
@ -281,14 +274,14 @@ public class Evaluator {
}
open override func matches(_ root: Element, _ element: Element)throws->Bool {
if(element.hasAttr(key)){
if(element.hasAttr(key)) {
let s = try element.attr(key)
return pattern.matcher(in:s).find()
}
return false
}
open override func toString()->String {
open override func toString() -> String {
return "[\(key)~=\(pattern.toString())]"
}
@ -297,9 +290,9 @@ public class Evaluator {
/**
* Abstract evaluator for attribute name/value matching
*/
public class AttributeKeyPair : Evaluator {
let key : String
var value : String
public class AttributeKeyPair: Evaluator {
let key: String
var value: String
public init(_ key: String, _ value2: String)throws {
var value2 = value2
@ -307,14 +300,13 @@ public class Evaluator {
try Validate.notEmpty(string: value2)
self.key = key.trim().lowercased()
if (value2.startsWith("\"") && value2.hasSuffix("\"") || value2.startsWith("'") && value2.hasSuffix("'"))
{
if (value2.startsWith("\"") && value2.hasSuffix("\"") || value2.startsWith("'") && value2.hasSuffix("'")) {
value2 = value2.substring(1, value2.characters.count-2)
}
self.value = value2.trim().lowercased()
}
open override func matches(_ root: Element, _ element: Element)throws->Bool{
open override func matches(_ root: Element, _ element: Element)throws->Bool {
preconditionFailure("self method must be overridden")
}
}
@ -322,13 +314,13 @@ public class Evaluator {
/**
* Evaluator for any / all element matching
*/
public final class AllElements : Evaluator {
public final class AllElements: Evaluator {
open override func matches(_ root: Element, _ element: Element)throws->Bool {
return true
}
open override func toString()->String {
open override func toString() -> String {
return "*"
}
}
@ -336,7 +328,7 @@ public class Evaluator {
/**
* Evaluator for matching by sibling index number (e {@literal <} idx)
*/
public final class IndexLessThan : IndexEvaluator {
public final class IndexLessThan: IndexEvaluator {
public override init(_ index: Int) {
super.init(index)
}
@ -345,7 +337,7 @@ public class Evaluator {
return try element.elementSiblingIndex() < index
}
open override func toString()->String {
open override func toString() -> String {
return ":lt(\(index))"
}
@ -354,7 +346,7 @@ public class Evaluator {
/**
* Evaluator for matching by sibling index number (e {@literal >} idx)
*/
public final class IndexGreaterThan : IndexEvaluator {
public final class IndexGreaterThan: IndexEvaluator {
public override init(_ index: Int) {
super.init(index)
}
@ -363,7 +355,7 @@ public class Evaluator {
return try element.elementSiblingIndex() > index
}
open override func toString()->String {
open override func toString() -> String {
return ":gt(\(index))"
}
@ -372,7 +364,7 @@ public class Evaluator {
/**
* Evaluator for matching by sibling index number (e = idx)
*/
public final class IndexEquals : IndexEvaluator {
public final class IndexEquals: IndexEvaluator {
public override init(_ index: Int) {
super.init(index)
}
@ -381,7 +373,7 @@ public class Evaluator {
return try element.elementSiblingIndex() == index
}
open override func toString()->String {
open override func toString() -> String {
return ":eq(\(index))"
}
@ -390,43 +382,42 @@ public class Evaluator {
/**
* Evaluator for matching the last sibling (css :last-child)
*/
public final class IsLastChild : Evaluator {
public final class IsLastChild: Evaluator {
open override func matches(_ root: Element, _ element: Element)throws->Bool {
if let p = element.parent(){
if let p = element.parent() {
let i = try element.elementSiblingIndex()
return !((p as? Document) != nil) && i == (p.getChildNodes().count - 1)
}
return false
}
open override func toString()->String {
open override func toString() -> String {
return ":last-child"
}
}
public final class IsFirstOfType : IsNthOfType {
public final class IsFirstOfType: IsNthOfType {
public init() {
super.init(0,1)
super.init(0, 1)
}
open override func toString()->String {
open override func toString() -> String {
return ":first-of-type"
}
}
public final class IsLastOfType : IsNthLastOfType {
public final class IsLastOfType: IsNthLastOfType {
public init() {
super.init(0,1)
super.init(0, 1)
}
open override func toString()->String {
open override func toString() -> String {
return ":last-of-type"
}
}
public class CssNthEvaluator : Evaluator {
public let a : Int
public let b : Int
public class CssNthEvaluator: Evaluator {
public let a: Int
public let b: Int
public init(_ a: Int, _ b: Int) {
self.a = a
@ -447,42 +438,40 @@ public class Evaluator {
return (pos-b)*a >= 0 && (pos-b)%a==0
}
open override func toString()->String {
if (a == 0){
open override func toString() -> String {
if (a == 0) {
return ":\(getPseudoClass)(\(b))"
}
if (b == 0){
if (b == 0) {
return ":\(getPseudoClass)(\(a))"
}
return ":\(getPseudoClass)(\(a)\(b))"
}
open func getPseudoClass()->String{
open func getPseudoClass() -> String {
preconditionFailure("self method must be overridden")
}
open func calculatePosition(_ root: Element, _ element: Element)throws->Int{
open func calculatePosition(_ root: Element, _ element: Element)throws->Int {
preconditionFailure("self method must be overridden")
}
}
/**
* css-compatible Evaluator for :eq (css :nth-child)
*
* @see IndexEquals
*/
public final class IsNthChild : CssNthEvaluator {
public final class IsNthChild: CssNthEvaluator {
public override init(_ a: Int, _ b: Int) {
super.init(a,b)
super.init(a, b)
}
open override func calculatePosition(_ root: Element, _ element: Element)throws->Int {
return try element.elementSiblingIndex()+1
}
open override func getPseudoClass()->String {
open override func getPseudoClass() -> String {
return "nth-child"
}
}
@ -492,22 +481,21 @@ public class Evaluator {
*
* @see IndexEquals
*/
public final class IsNthLastChild : CssNthEvaluator {
public final class IsNthLastChild: CssNthEvaluator {
public override init(_ a: Int, _ b: Int) {
super.init(a,b)
super.init(a, b)
}
open override func calculatePosition(_ root: Element, _ element: Element)throws->Int
{
open override func calculatePosition(_ root: Element, _ element: Element)throws->Int {
var i = 0
if let l = element.parent(){
if let l = element.parent() {
i = l.children().array().count
}
return i - (try element.elementSiblingIndex())
}
open override func getPseudoClass()->String {
open override func getPseudoClass() -> String {
return "nth-last-child"
}
}
@ -516,32 +504,30 @@ public class Evaluator {
* css pseudo class nth-of-type
*
*/
public class IsNthOfType : CssNthEvaluator {
public class IsNthOfType: CssNthEvaluator {
public override init(_ a: Int, _ b: Int) {
super.init(a,b)
super.init(a, b)
}
open override func calculatePosition(_ root: Element, _ element: Element)->Int {
open override func calculatePosition(_ root: Element, _ element: Element) -> Int {
var pos = 0
let family: Elements? = element.parent()?.children()
if let array = family?.array(){
for el in array
{
if let array = family?.array() {
for el in array {
if (el.tag() == element.tag()) {pos+=1}
if (el === element) {break}
}
}
return pos
}
open override func getPseudoClass()->String {
open override func getPseudoClass() -> String {
return "nth-of-type"
}
}
public class IsNthLastOfType : CssNthEvaluator {
public class IsNthLastOfType: CssNthEvaluator {
public override init(_ a: Int, _ b: Int) {
super.init(a, b)
@ -549,10 +535,10 @@ public class Evaluator {
open override func calculatePosition(_ root: Element, _ element: Element)throws->Int {
var pos = 0
if let family = element.parent()?.children(){
if let family = element.parent()?.children() {
let x = try element.elementSiblingIndex()
for i in x..<family.array().count {
if (family.get(i).tag() == element.tag()){
if (family.get(i).tag() == element.tag()) {
pos+=1
}
}
@ -561,7 +547,7 @@ public class Evaluator {
return pos
}
open override func getPseudoClass()->String {
open override func getPseudoClass() -> String {
return "nth-last-of-type"
}
}
@ -569,16 +555,16 @@ public class Evaluator {
/**
* Evaluator for matching the first sibling (css :first-child)
*/
public final class IsFirstChild : Evaluator {
public final class IsFirstChild: Evaluator {
open override func matches(_ root: Element, _ element: Element)throws->Bool {
let p = element.parent()
if(p != nil && !(((p as? Document) != nil))){
if(p != nil && !(((p as? Document) != nil))) {
return (try element.elementSiblingIndex()) == 0
}
return false
}
open override func toString()->String {
open override func toString() -> String {
return ":first-child"
}
}
@ -588,54 +574,53 @@ public class Evaluator {
* @see <a href="http://www.w3.org/TR/selectors/#root-pseudo">:root selector</a>
*
*/
public final class IsRoot : Evaluator {
public final class IsRoot: Evaluator {
open override func matches(_ root: Element, _ element: Element)throws->Bool {
let r: Element = ((root as? Document) != nil) ? root.child(0) : root
return element === r
}
open override func toString()->String {
open override func toString() -> String {
return ":root"
}
}
public final class IsOnlyChild : Evaluator {
public final class IsOnlyChild: Evaluator {
open override func matches(_ root: Element, _ element: Element)throws->Bool {
let p = element.parent()
return p != nil && !((p as? Document) != nil) && element.siblingElements().array().count == 0
}
open override func toString()->String {
open override func toString() -> String {
return ":only-child"
}
}
public final class IsOnlyOfType : Evaluator {
public final class IsOnlyOfType: Evaluator {
open override func matches(_ root: Element, _ element: Element)throws->Bool {
let p = element.parent()
if (p == nil || (p as? Document) != nil) {return false}
var pos = 0
if let family = p?.children().array(){
if let family = p?.children().array() {
for el in family {
if (el.tag() == element.tag()) {pos+=1}
}
}
return pos == 1
}
open override func toString()->String {
open override func toString() -> String {
return ":only-of-type"
}
}
public final class IsEmpty : Evaluator {
public final class IsEmpty: Evaluator {
open override func matches(_ root: Element, _ element: Element)throws->Bool {
let family: Array<Node> = element.getChildNodes()
for n in family
{
for n in family {
if (!((n as? Comment) != nil || (n as? XmlDeclaration) != nil || (n as? DocumentType) != nil)) {return false}
}
return true
}
open override func toString()->String {
open override func toString() -> String {
return ":empty"
}
}
@ -645,7 +630,7 @@ public class Evaluator {
*
* @author ant
*/
public class IndexEvaluator : Evaluator {
public class IndexEvaluator: Evaluator {
let index: Int
public init(_ index: Int) {
@ -656,7 +641,7 @@ public class Evaluator {
/**
* Evaluator for matching Element (and its descendants) text
*/
public final class ContainsText : Evaluator {
public final class ContainsText: Evaluator {
private let searchText: String
public init(_ searchText: String) {
@ -667,7 +652,7 @@ public class Evaluator {
return (try element.text().lowercased().contains(searchText))
}
open override func toString()->String {
open override func toString() -> String {
return ":contains(\(searchText)"
}
}
@ -675,8 +660,8 @@ public class Evaluator {
/**
* Evaluator for matching Element's own text
*/
public final class ContainsOwnText : Evaluator {
private let searchText : String
public final class ContainsOwnText: Evaluator {
private let searchText: String
public init(_ searchText: String) {
self.searchText = searchText.lowercased()
@ -686,7 +671,7 @@ public class Evaluator {
return (element.ownText().lowercased().contains(searchText))
}
open override func toString()->String {
open override func toString() -> String {
return ":containsOwn(\(searchText)"
}
}
@ -694,7 +679,7 @@ public class Evaluator {
/**
* Evaluator for matching Element (and its descendants) text with regex
*/
public final class Matches : Evaluator {
public final class Matches: Evaluator {
private let pattern: Pattern
public init(_ pattern: Pattern) {
@ -706,7 +691,7 @@ public class Evaluator {
return m.find()
}
open override func toString()->String {
open override func toString() -> String {
return ":matches(\(pattern)"
}
}
@ -714,7 +699,7 @@ public class Evaluator {
/**
* Evaluator for matching Element's own text with regex
*/
public final class MatchesOwn : Evaluator {
public final class MatchesOwn: Evaluator {
private let pattern: Pattern
public init(_ pattern: Pattern) {
@ -726,7 +711,7 @@ public class Evaluator {
return m.find()
}
open override func toString()->String {
open override func toString() -> String {
return ":matchesOwn(\(pattern.toString())"
}
}

View File

@ -17,8 +17,6 @@ public enum ExceptionType {
case SelectorParseException
}
public enum Exception : Error {
case Error(type:ExceptionType ,Message: String)
public enum Exception: Error {
case Error(type:ExceptionType, Message: String)
}

View File

@ -12,7 +12,7 @@ import Foundation
* A HTML Form Element provides ready access to the form fields/controls that are associated with it. It also allows a
* form to easily be submitted.
*/
public class FormElement : Element {
public class FormElement: Element {
private let _elements: Elements = Elements()
/**
@ -30,7 +30,7 @@ public class FormElement : Element {
* Get the list of form control elements associated with this form.
* @return form controls associated with this element.
*/
public func elements()->Elements {
public func elements() -> Elements {
return _elements
}
@ -40,7 +40,7 @@ public class FormElement : Element {
* @return this form element, for chaining
*/
@discardableResult
public func addElement(_ element: Element)->FormElement {
public func addElement(_ element: Element) -> FormElement {
_elements.add(element)
return self
}
@ -106,23 +106,20 @@ public class FormElement : Element {
// return data;
// }
public override func copy(with zone: NSZone? = nil) -> Any
{
let clone = FormElement(_tag,baseUri!,attributes!)
public override func copy(with zone: NSZone? = nil) -> Any {
let clone = FormElement(_tag, baseUri!, attributes!)
return copy(clone: clone)
}
public override func copy(parent: Node?)->Node
{
let clone = FormElement(_tag,baseUri!,attributes!)
return copy(clone: clone,parent: parent)
public override func copy(parent: Node?) -> Node {
let clone = FormElement(_tag, baseUri!, attributes!)
return copy(clone: clone, parent: parent)
}
public override func copy(clone: Node, parent: Node?)->Node
{
public override func copy(clone: Node, parent: Node?) -> Node {
let clone = clone as! FormElement
for att in _elements.array(){
for att in _elements.array() {
clone._elements.add(att)
}
return super.copy(clone: clone,parent: parent)
return super.copy(clone: clone, parent: parent)
}
}

View File

@ -43,13 +43,11 @@ class HtmlTreeBuilder: TreeBuilder {
private var fosterInserts: Bool = false // if next inserts should be fostered
private var fragmentParsing: Bool = false // if parsing a fragment of html
public override init() {
super.init()
}
public override func defaultSettings()->ParseSettings {
public override func defaultSettings() -> ParseSettings {
return ParseSettings.htmlDefault
}
@ -74,17 +72,17 @@ class HtmlTreeBuilder: TreeBuilder {
// initialise the tokeniser state:
let contextTag: String = context.tagName()
if (StringUtil.inString(contextTag, haystack: "title", "textarea")){
if (StringUtil.inString(contextTag, haystack: "title", "textarea")) {
tokeniser.transition(TokeniserState.Rcdata)
}else if (StringUtil.inString(contextTag, haystack: "iframe", "noembed", "noframes", "style", "xmp")){
} else if (StringUtil.inString(contextTag, haystack: "iframe", "noembed", "noframes", "style", "xmp")) {
tokeniser.transition(TokeniserState.Rawtext)
}else if (contextTag=="script"){
} else if (contextTag=="script") {
tokeniser.transition(TokeniserState.ScriptData)
}else if (contextTag==("noscript")){
} else if (contextTag==("noscript")) {
tokeniser.transition(TokeniserState.Data) // if scripting enabled, rawtext
}else if (contextTag=="plaintext"){
} else if (contextTag=="plaintext") {
tokeniser.transition(TokeniserState.Data)
}else{
} else {
tokeniser.transition(TokeniserState.Data) // default
}
@ -98,7 +96,7 @@ class HtmlTreeBuilder: TreeBuilder {
// with form correctly
let contextChain: Elements = context.parents()
contextChain.add(0, context)
for parent:Element in contextChain.array() {
for parent: Element in contextChain.array() {
if let x = (parent as? FormElement) {
formElement = x
break
@ -107,15 +105,15 @@ class HtmlTreeBuilder: TreeBuilder {
}
try runParser()
if (context != nil && root != nil){
if (context != nil && root != nil) {
return root!.getChildNodes()
}else{
} else {
return doc.getChildNodes()
}
}
@discardableResult
public override func process(_ token: Token)throws->Bool{
public override func process(_ token: Token)throws->Bool {
currentToken = token
return try self._state.process(token, self)
}
@ -130,7 +128,7 @@ class HtmlTreeBuilder: TreeBuilder {
self._state = state
}
func state()->HtmlTreeBuilderState {
func state() -> HtmlTreeBuilderState {
return _state
}
@ -138,7 +136,7 @@ class HtmlTreeBuilder: TreeBuilder {
_originalState = _state
}
func originalState()->HtmlTreeBuilderState {
func originalState() -> HtmlTreeBuilderState {
return _originalState
}
@ -146,20 +144,20 @@ class HtmlTreeBuilder: TreeBuilder {
self._framesetOk = framesetOk
}
func framesetOk()->Bool {
func framesetOk() -> Bool {
return _framesetOk
}
func getDocument()->Document {
func getDocument() -> Document {
return doc
}
func getBaseUri()->String {
func getBaseUri() -> String {
return baseUri
}
func maybeSetBaseUri(_ base: Element)throws {
if (baseUriSetFromDoc){ // only listen to the first <base href> in parse
if (baseUriSetFromDoc) { // only listen to the first <base href> in parse
return
}
@ -171,12 +169,12 @@ class HtmlTreeBuilder: TreeBuilder {
}
}
func isFragmentParsing()->Bool {
func isFragmentParsing() -> Bool {
return fragmentParsing
}
func error(_ state: HtmlTreeBuilderState) {
if (errors.canAddError() && currentToken != nil){
if (errors.canAddError() && currentToken != nil) {
errors.add(ParseError(reader.getPos(), "Unexpected token [\(currentToken!.tokenType())] when in state [\(state.rawValue)]"))
}
}
@ -235,7 +233,7 @@ class HtmlTreeBuilder: TreeBuilder {
let el: FormElement = FormElement(tag, baseUri, startTag._attributes)
setFormElement(el)
try insertNode(el)
if (onStack){
if (onStack) {
stack.append(el)
}
return el
@ -250,10 +248,10 @@ class HtmlTreeBuilder: TreeBuilder {
var node: Node
// characters in script and style go in as datanodes, not text nodes
let tagName: String? = currentElement()?.tagName()
if (tagName=="script" || tagName=="style"){
if (tagName=="script" || tagName=="style") {
try Validate.notNull(obj: characterToken.getData())
node = DataNode(characterToken.getData()!, baseUri)
}else{
} else {
try Validate.notNull(obj: characterToken.getData())
node = TextNode(characterToken.getData()!, baseUri)
}
@ -262,18 +260,18 @@ class HtmlTreeBuilder: TreeBuilder {
private func insertNode(_ node: Node)throws {
// if the stack hasn't been set up yet, elements (doctype, comments) go into the doc
if (stack.count == 0){
if (stack.count == 0) {
try doc.appendChild(node)
}else if (isFosterInserts()){
} else if (isFosterInserts()) {
try insertInFosterParent(node)
}else{
} else {
try currentElement()?.appendChild(node)
}
// connect form controls to their form element
if let n = (node as? Element) {
if(n.tag().isFormListed()){
if ( formElement != nil){
if(n.tag().isFormListed()) {
if ( formElement != nil) {
formElement!.addElement(n)
}
}
@ -281,7 +279,7 @@ class HtmlTreeBuilder: TreeBuilder {
}
@discardableResult
func pop()->Element {
func pop() -> Element {
let size: Int = stack.count
return stack.remove(at: size-1)
}
@ -295,11 +293,11 @@ class HtmlTreeBuilder: TreeBuilder {
}
@discardableResult
func onStack(_ el: Element)->Bool {
func onStack(_ el: Element) -> Bool {
return isElementInQueue(stack, el)
}
private func isElementInQueue(_ queue: Array<Element?>, _ element: Element?)->Bool {
private func isElementInQueue(_ queue: Array<Element?>, _ element: Element?) -> Bool {
for pos in (0..<queue.count).reversed() {
let next: Element? = queue[pos]
if (next == element) {
@ -309,7 +307,7 @@ class HtmlTreeBuilder: TreeBuilder {
return false
}
func getFromStack(_ elName: String)->Element? {
func getFromStack(_ elName: String) -> Element? {
for pos in (0..<stack.count).reversed() {
let next: Element = stack[pos]
if next.nodeName() == elName {
@ -320,7 +318,7 @@ class HtmlTreeBuilder: TreeBuilder {
}
@discardableResult
func removeFromStack(_ el: Element)->Bool {
func removeFromStack(_ el: Element) -> Bool {
for pos in (0..<stack.count).reversed() {
let next: Element = stack[pos]
if (next == el) {
@ -335,7 +333,7 @@ class HtmlTreeBuilder: TreeBuilder {
for pos in (0..<stack.count).reversed() {
let next: Element = stack[pos]
stack.remove(at: pos)
if (next.nodeName() == elName){
if (next.nodeName() == elName) {
break
}
}
@ -348,7 +346,7 @@ class HtmlTreeBuilder: TreeBuilder {
for pos in (0..<stack.count).reversed() {
let next: Element = stack[pos]
stack.remove(at: pos)
if (StringUtil.inString(next.nodeName(),elNames)){
if (StringUtil.inString(next.nodeName(), elNames)) {
break
}
}
@ -383,15 +381,15 @@ class HtmlTreeBuilder: TreeBuilder {
private func clearStackToContext(_ nodeNames: [String]) {
for pos in (0..<stack.count).reversed() {
let next: Element = stack[pos]
if (StringUtil.inString(next.nodeName(), nodeNames) || next.nodeName()=="html"){
if (StringUtil.inString(next.nodeName(), nodeNames) || next.nodeName()=="html") {
break
}else{
} else {
stack.remove(at: pos)
}
}
}
func aboveOnStack(_ el: Element)->Element? {
func aboveOnStack(_ el: Element) -> Element? {
//assert(onStack(el), "Invalid parameter")
onStack(el)
for pos in (0..<stack.count).reversed() {
@ -479,17 +477,17 @@ class HtmlTreeBuilder: TreeBuilder {
return try inSpecificScope(specificScopeTarget, baseTypes, extraTypes)
}
private func inSpecificScope(_ targetNames: [String?], _ baseTypes: [String] , _ extraTypes: [String]?)throws->Bool {
private func inSpecificScope(_ targetNames: [String?], _ baseTypes: [String], _ extraTypes: [String]?)throws->Bool {
for pos in (0..<stack.count).reversed() {
let el: Element = stack[pos]
let elName: String = el.nodeName()
if (StringUtil.inString(elName, targetNames)){
if (StringUtil.inString(elName, targetNames)) {
return true
}
if (StringUtil.inString(elName, baseTypes)){
if (StringUtil.inString(elName, baseTypes)) {
return false
}
if (extraTypes != nil && StringUtil.inString(elName, extraTypes!)){
if (extraTypes != nil && StringUtil.inString(elName, extraTypes!)) {
return false
}
}
@ -527,10 +525,10 @@ class HtmlTreeBuilder: TreeBuilder {
for pos in (0..<stack.count).reversed() {
let el: Element = stack[pos]
let elName: String = el.nodeName()
if (elName.equals(targetName)){
if (elName.equals(targetName)) {
return true
}
if (!StringUtil.inString(elName, HtmlTreeBuilder.TagSearchSelectScope)){ // all elements except
if (!StringUtil.inString(elName, HtmlTreeBuilder.TagSearchSelectScope)) { // all elements except
return false
}
}
@ -542,11 +540,11 @@ class HtmlTreeBuilder: TreeBuilder {
self.headElement = headElement
}
func getHeadElement()->Element? {
func getHeadElement() -> Element? {
return headElement
}
func isFosterInserts()->Bool {
func isFosterInserts() -> Bool {
return fosterInserts
}
@ -554,7 +552,7 @@ class HtmlTreeBuilder: TreeBuilder {
self.fosterInserts = fosterInserts
}
func getFormElement()->FormElement? {
func getFormElement() -> FormElement? {
return formElement
}
@ -587,7 +585,7 @@ class HtmlTreeBuilder: TreeBuilder {
func generateImpliedEndTags(_ excludeTag: String?) {
while ((excludeTag != nil && !currentElement()!.nodeName().equals(excludeTag!)) &&
StringUtil.inString(currentElement()!.nodeName(), HtmlTreeBuilder.TagSearchEndTags)){
StringUtil.inString(currentElement()!.nodeName(), HtmlTreeBuilder.TagSearchEndTags)) {
pop()
}
}
@ -596,22 +594,22 @@ class HtmlTreeBuilder: TreeBuilder {
generateImpliedEndTags(nil)
}
func isSpecial(_ el: Element)->Bool {
func isSpecial(_ el: Element) -> Bool {
// todo: mathml's mi, mo, mn
// todo: svg's foreigObject, desc, title
let name: String = el.nodeName()
return StringUtil.inString(name, HtmlTreeBuilder.TagSearchSpecial)
}
func lastFormattingElement()->Element? {
func lastFormattingElement() -> Element? {
return formattingElements.count > 0 ? formattingElements[formattingElements.count-1] : nil
}
func removeLastFormattingElement()->Element? {
func removeLastFormattingElement() -> Element? {
let size: Int = formattingElements.count
if (size > 0){
if (size > 0) {
return formattingElements.remove(at: size-1)
}else{
} else {
return nil
}
}
@ -621,11 +619,11 @@ class HtmlTreeBuilder: TreeBuilder {
var numSeen: Int = 0
for pos in (0..<formattingElements.count).reversed() {
let el: Element? = formattingElements[pos]
if (el == nil){ // marker
if (el == nil) { // marker
break
}
if (isSameFormattingElement(input, el!)){
if (isSameFormattingElement(input, el!)) {
numSeen += 1
}
@ -637,9 +635,9 @@ class HtmlTreeBuilder: TreeBuilder {
formattingElements.append(input)
}
private func isSameFormattingElement(_ a: Element, _ b: Element)->Bool {
private func isSameFormattingElement(_ a: Element, _ b: Element) -> Bool {
// same if: same namespace, tag, and attributes. Element.equals only checks tag, might in future check children
if(a.attributes == nil){
if(a.attributes == nil) {
return false
}
@ -651,14 +649,13 @@ class HtmlTreeBuilder: TreeBuilder {
func reconstructFormattingElements()throws {
let last: Element? = lastFormattingElement()
if (last == nil || onStack(last!)){
if (last == nil || onStack(last!)) {
return
}
var entry:Element? = last
let size:Int = formattingElements.count
var pos:Int = size - 1
var entry: Element? = last
let size: Int = formattingElements.count
var pos: Int = size - 1
var skip: Bool = false
while (true) {
if (pos == 0) { // step 4. if none before, skip to 8
@ -696,7 +693,7 @@ class HtmlTreeBuilder: TreeBuilder {
func clearFormattingElementsToLastMarker() {
while (!formattingElements.isEmpty) {
let el: Element? = removeLastFormattingElement()
if (el == nil){
if (el == nil) {
break
}
}
@ -712,27 +709,22 @@ class HtmlTreeBuilder: TreeBuilder {
}
}
func isInActiveFormattingElements(_ el: Element)->Bool {
func isInActiveFormattingElements(_ el: Element) -> Bool {
return isElementInQueue(formattingElements, el)
}
func getActiveFormattingElement(_ nodeName: String)->Element? {
func getActiveFormattingElement(_ nodeName: String) -> Element? {
for pos in (0..<formattingElements.count).reversed() {
let next: Element? = formattingElements[pos]
if (next == nil){ // scope marker
if (next == nil) { // scope marker
break
}else if (next!.nodeName().equals(nodeName)){
} else if (next!.nodeName().equals(nodeName)) {
return next
}
}
return nil
}
func replaceActiveFormattingElement(_ out: Element, _ input: Element)throws {
try formattingElements = replaceInQueue(formattingElements as! Array<Element>, out, input)//todo: testare as! non è bello
}
@ -749,7 +741,7 @@ class HtmlTreeBuilder: TreeBuilder {
if (lastTable.parent() != nil) {
fosterParent = lastTable.parent()!
isLastTableParent = true
} else{
} else {
fosterParent = aboveOnStack(lastTable)
}
} else { // no table == frag
@ -759,8 +751,7 @@ class HtmlTreeBuilder: TreeBuilder {
if (isLastTableParent) {
try Validate.notNull(obj: lastTable) // last table cannot be null by this point.
try lastTable!.before(input)
}
else{
} else {
try fosterParent?.appendChild(input)
}
}

View File

@ -12,8 +12,7 @@ protocol HtmlTreeBuilderStateProtocol {
func process(_ t: Token, _ tb: HtmlTreeBuilder)throws->Bool
}
enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
{
enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol {
case Initial
case BeforeHtml
case BeforeHead
@ -40,13 +39,11 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
private static let nullString: String = "\u{0000}"
public func equals(_ s: HtmlTreeBuilderState)->Bool
{
public func equals(_ s: HtmlTreeBuilderState) -> Bool {
return self.hashValue == s.hashValue
}
func process(_ t: Token, _ tb: HtmlTreeBuilder)throws->Bool
{
func process(_ t: Token, _ tb: HtmlTreeBuilder)throws->Bool {
switch self {
case .Initial:
if (HtmlTreeBuilderState.isWhitespace(t)) {
@ -60,7 +57,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
let doctype: DocumentType = DocumentType(
tb.settings.normalizeTag(d.getName()), d.getPublicIdentifier(), d.getSystemIdentifier(), tb.getBaseUri())
try tb.getDocument().appendChild(doctype)
if (d.isForceQuirks()){
if (d.isForceQuirks()) {
tb.getDocument().quirksMode(Document.QuirksMode.quirks)
}
tb.transition(.BeforeHtml)
@ -147,7 +144,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
} else if (StringUtil.inString(name, haystack: "base", "basefont", "bgsound", "command", "link")) {
let el: Element = try tb.insertEmpty(start)
// jsoup special: update base the frist time it is seen
if (name.equals("base") && el.hasAttr("href")){
if (name.equals("base") && el.hasAttr("href")) {
try tb.maybeSetBaseUri(el)
}
} else if (name.equals("meta")) {
@ -267,15 +264,14 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
}
return true
case .InBody:
func anyOtherEndTag(_ t: Token, _ tb: HtmlTreeBuilder)->Bool
{
func anyOtherEndTag(_ t: Token, _ tb: HtmlTreeBuilder) -> Bool {
let name: String? = t.asEndTag().normalName()
let stack: Array<Element> = tb.getStack()
for pos in (0..<stack.count).reversed(){
for pos in (0..<stack.count).reversed() {
let node: Element = stack[pos]
if (name != nil && node.nodeName().equals(name!)) {
tb.generateImpliedEndTags(name)
if (!name!.equals((tb.currentElement()?.nodeName())!)){
if (!name!.equals((tb.currentElement()?.nodeName())!)) {
tb.error(self)
}
tb.popStackToClose(name!)
@ -290,8 +286,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
return true
}
switch (t.type)
{
switch (t.type) {
case Token.TokenType.Char:
let c: Token.Char = t.asCharacter()
if (c.getData() != nil && c.getData()!.equals(HtmlTreeBuilderState.nullString)) {
@ -315,8 +310,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
return false
case Token.TokenType.StartTag:
let startTag: Token.StartTag = t.asStartTag()
if let name: String = startTag.normalName()
{
if let name: String = startTag.normalName() {
if (name.equals("a")) {
if (tb.getActiveFormattingElement("a") != nil) {
tb.error(self)
@ -354,7 +348,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
try tb.processEndTag("li")
break
}
if (tb.isSpecial(el) && !StringUtil.inSorted(el.nodeName(), haystack: Constants.InBodyStartLiBreakers)){
if (tb.isSpecial(el) && !StringUtil.inSorted(el.nodeName(), haystack: Constants.InBodyStartLiBreakers)) {
break
}
}
@ -367,7 +361,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
// merge attributes onto real html
let html: Element = tb.getStack()[0]
for attribute in startTag.getAttributes().iterator() {
if (!html.hasAttr(attribute.getKey())){
if (!html.hasAttr(attribute.getKey())) {
html.getAttributes()?.put(attribute: attribute)
}
}
@ -375,7 +369,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
return try tb.process(t, .InHead)
} else if (name.equals("body")) {
tb.error(self)
let stack : Array<Element> = tb.getStack()
let stack: Array<Element> = tb.getStack()
if (stack.count == 1 || (stack.count > 2 && !stack[1].nodeName().equals("body"))) {
// only in fragment case
return false // ignore
@ -383,7 +377,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
tb.framesetOk(false)
let body: Element = stack[1]
for attribute: Attribute in startTag.getAttributes().iterator() {
if (!body.hasAttr(attribute.getKey())){
if (!body.hasAttr(attribute.getKey())) {
body.getAttributes()?.put(attribute: attribute)
}
}
@ -398,11 +392,11 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
return false // ignore frameset
} else {
let second: Element = stack[1]
if (second.parent() != nil){
if (second.parent() != nil) {
try second.remove()
}
// pop up to html element
while (stack.count > 1){
while (stack.count > 1) {
stack.remove(at: stack.count-1)
}
try tb.insert(startTag)
@ -435,14 +429,14 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
try tb.insertForm(startTag, true)
} else if (StringUtil.inSorted(name, haystack: Constants.DdDt)) {
tb.framesetOk(false)
let stack:Array<Element> = tb.getStack()
let stack: Array<Element> = tb.getStack()
for i in (1..<stack.count).reversed() {
let el: Element = stack[i]
if (StringUtil.inSorted(el.nodeName(), haystack: Constants.DdDt)) {
try tb.processEndTag(el.nodeName())
break
}
if (tb.isSpecial(el) && !StringUtil.inSorted(el.nodeName(), haystack: Constants.InBodyStartLiBreakers)){
if (tb.isSpecial(el) && !StringUtil.inSorted(el.nodeName(), haystack: Constants.InBodyStartLiBreakers)) {
break
}
}
@ -495,7 +489,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
} else if (name.equals("input")) {
try tb.reconstructFormattingElements()
let el: Element = try tb.insertEmpty(startTag)
if (try !el.attr("type").equalsIgnoreCase(string: "hidden")){
if (try !el.attr("type").equalsIgnoreCase(string: "hidden")) {
tb.framesetOk(false)
}
} else if (StringUtil.inSorted(name, haystack: Constants.InBodyStartMedia)) {
@ -507,22 +501,22 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
try tb.insertEmpty(startTag)
tb.framesetOk(false)
} else if (name.equals("image")) {
if (tb.getFromStack("svg") == nil){
if (tb.getFromStack("svg") == nil) {
return try tb.process(startTag.name("img")) // change <image> to <img>, unless in svg
}else{
} else {
try tb.insert(startTag)
}
} else if (name.equals("isindex")) {
// how much do we care about the early 90s?
tb.error(self)
if (tb.getFormElement() != nil){
if (tb.getFormElement() != nil) {
return false
}
tb.tokeniser.acknowledgeSelfClosingFlag()
try tb.processStartTag("form")
if (startTag._attributes.hasKey(key: "action")) {
if let form: Element = tb.getFormElement(){
if let form: Element = tb.getFormElement() {
try form.attr("action", startTag._attributes.get(key: "action"))
}
}
@ -538,7 +532,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
// input
let inputAttribs: Attributes = Attributes()
for attr: Attribute in startTag._attributes.iterator() {
if (!StringUtil.inSorted(attr.getKey(), haystack: Constants.InBodyStartInputAttribs)){
if (!StringUtil.inSorted(attr.getKey(), haystack: Constants.InBodyStartInputAttribs)) {
inputAttribs.put(attribute: attr)
}
}
@ -573,13 +567,13 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
tb.framesetOk(false)
let state: HtmlTreeBuilderState = tb.state()
if (state.equals(.InTable) || state.equals(.InCaption) || state.equals(.InTableBody) || state.equals(.InRow) || state.equals(.InCell)){
if (state.equals(.InTable) || state.equals(.InCaption) || state.equals(.InTableBody) || state.equals(.InRow) || state.equals(.InCell)) {
tb.transition(.InSelectInTable)
}else{
} else {
tb.transition(.InSelect)
}
} else if (StringUtil.inSorted(name, haystack: Constants.InBodyStartOptions)) {
if (tb.currentElement() != nil && tb.currentElement()!.nodeName().equals("option")){
if (tb.currentElement() != nil && tb.currentElement()!.nodeName().equals("option")) {
try tb.processEndTag("option")
}
try tb.reconstructFormattingElements()
@ -610,7 +604,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
try tb.reconstructFormattingElements()
try tb.insert(startTag)
}
}else{
} else {
try tb.reconstructFormattingElements()
try tb.insert(startTag)
}
@ -618,22 +612,21 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
case .EndTag:
let endTag: Token.EndTag = t.asEndTag()
if let name = endTag.normalName(){
if let name = endTag.normalName() {
if (StringUtil.inSorted(name, haystack: Constants.InBodyEndAdoptionFormatters)) {
// Adoption Agency Algorithm.
for i in 0..<8
{
for i in 0..<8 {
let formatEl: Element? = tb.getActiveFormattingElement(name)
if (formatEl == nil){
if (formatEl == nil) {
return anyOtherEndTag(t, tb)
}else if (!tb.onStack(formatEl!)) {
} else if (!tb.onStack(formatEl!)) {
tb.error(self)
tb.removeFromActiveFormattingElements(formatEl!)
return true
} else if (try !tb.inScope(formatEl!.nodeName())) {
tb.error(self)
return false
} else if (tb.currentElement() != formatEl!){
} else if (tb.currentElement() != formatEl!) {
tb.error(self)
}
@ -644,9 +637,8 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
// the spec doesn't limit to < 64, but in degenerate cases (9000+ stack depth) self prevents
// run-aways
var stackSize = stack.count
if(stackSize > 64){stackSize = 64}
for si in 0..<stackSize
{
if(stackSize > 64) {stackSize = 64}
for si in 0..<stackSize {
let el: Element = stack[si]
if (el == formatEl) {
commonAncestor = stack[si - 1]
@ -666,17 +658,15 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
// does that mean: int pos of format el in list?
var node: Element? = furthestBlock
var lastNode: Element? = furthestBlock
for j in 0..<3
{
if (node != nil && tb.onStack(node!)){
for j in 0..<3 {
if (node != nil && tb.onStack(node!)) {
node = tb.aboveOnStack(node!)
}
// note no bookmark check
if (node != nil && !tb.isInActiveFormattingElements(node!))
{
if (node != nil && !tb.isInActiveFormattingElements(node!)) {
tb.removeFromStack(node!)
continue
} else if (node == formatEl){
} else if (node == formatEl) {
break
}
@ -690,7 +680,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
// todo: move the aforementioned bookmark to be immediately after the node in the list of active formatting elements.
// not getting how self bookmark both straddles the element above, but is inbetween here...
}
if (lastNode!.parent() != nil){
if (lastNode!.parent() != nil) {
try lastNode?.remove()
}
try node!.appendChild(lastNode!)
@ -699,12 +689,12 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
}
if (StringUtil.inSorted(commonAncestor!.nodeName(), haystack: Constants.InBodyEndTableFosters)) {
if (lastNode!.parent() != nil){
if (lastNode!.parent() != nil) {
try lastNode!.remove()
}
try tb.insertInFosterParent(lastNode!)
} else {
if (lastNode!.parent() != nil){
if (lastNode!.parent() != nil) {
try lastNode!.remove()
}
try commonAncestor!.appendChild(lastNode!)
@ -713,8 +703,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
let adopter: Element = Element(formatEl!.tag(), tb.getBaseUri())
adopter.getAttributes()?.addAll(incoming: formatEl!.getAttributes())
var childNodes: [Node] = furthestBlock!.getChildNodes()
for childNode: Node in childNodes
{
for childNode: Node in childNodes {
try adopter.appendChild(childNode) // append will reparent. thus the clone to avoid concurrent mod.
}
try furthestBlock?.appendChild(adopter)
@ -730,7 +719,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
return false
} else {
tb.generateImpliedEndTags()
if (!tb.currentElement()!.nodeName().equals(name)){
if (!tb.currentElement()!.nodeName().equals(name)) {
tb.error(self)
}
tb.popStackToClose(name)
@ -744,7 +733,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
return false
} else {
tb.generateImpliedEndTags(name)
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)){
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)) {
tb.error(self)
}
tb.popStackToClose(name)
@ -759,7 +748,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
}
} else if (name.equals("html")) {
let notIgnored: Bool = try tb.processEndTag("body")
if (notIgnored){
if (notIgnored) {
return try tb.process(endTag)
}
} else if (name.equals("form")) {
@ -770,7 +759,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
return false
} else {
tb.generateImpliedEndTags()
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)){
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)) {
tb.error(self)
}
// remove currentForm from stack. will shift anything under up.
@ -783,7 +772,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
return try tb.process(endTag)
} else {
tb.generateImpliedEndTags(name)
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)){
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)) {
tb.error(self)
}
tb.popStackToClose(name)
@ -794,7 +783,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
return false
} else {
tb.generateImpliedEndTags(name)
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)){
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)) {
tb.error(self)
}
tb.popStackToClose(name)
@ -805,7 +794,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
return false
} else {
tb.generateImpliedEndTags(name)
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)){
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)) {
tb.error(self)
}
tb.popStackToClose(Constants.Headings)
@ -820,7 +809,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
return false
}
tb.generateImpliedEndTags()
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)){
if (tb.currentElement() != nil && !tb.currentElement()!.nodeName().equals(name)) {
tb.error(self)
}
tb.popStackToClose(name)
@ -833,7 +822,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
} else {
return anyOtherEndTag(t, tb)
}
}else{
} else {
return anyOtherEndTag(t, tb)
}
@ -886,8 +875,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
return false
} else if (t.isStartTag()) {
let startTag: Token.StartTag = t.asStartTag()
if let name: String = startTag.normalName()
{
if let name: String = startTag.normalName() {
if (name.equals("caption")) {
tb.clearStackToTableContext()
tb.insertMarkerToFormattingElements()
@ -922,9 +910,9 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
}
} else if (name.equals("form")) {
tb.error(self)
if (tb.getFormElement() != nil){
if (tb.getFormElement() != nil) {
return false
}else {
} else {
try tb.insertForm(startTag, false)
}
} else {
@ -934,8 +922,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
return true // todo: check if should return processed http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#parsing-main-intable
} else if (t.isEndTag()) {
let endTag: Token.EndTag = t.asEndTag()
if let name: String = endTag.normalName()
{
if let name: String = endTag.normalName() {
if (name.equals("table")) {
if (try !tb.inTableScope(name)) {
tb.error(self)
@ -951,12 +938,12 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
} else {
return try anythingElse(t, tb)
}
}else{
} else {
return try anythingElse(t, tb)
}
return true // todo: as above todo
} else if (t.isEOF()) {
if (tb.currentElement() != nil && tb.currentElement()!.nodeName().equals("html")){
if (tb.currentElement() != nil && tb.currentElement()!.nodeName().equals("html")) {
tb.error(self)
}
return true // stops parsing
@ -978,8 +965,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
default:
// todo - don't really like the way these table character data lists are built
if (tb.getPendingTableCharacters().count > 0) {
for character:String in tb.getPendingTableCharacters()
{
for character: String in tb.getPendingTableCharacters() {
if (!HtmlTreeBuilderState.isWhitespace(character)) {
// InTable anything else section:
tb.error(self)
@ -990,7 +976,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
} else {
try tb.process(Token.Char().data(character), .InBody)
}
} else{
} else {
try tb.insert(Token.Char().data(character))
}
}
@ -1001,8 +987,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
}
return true
case .InCaption:
if (t.isEndTag() && t.asEndTag().normalName()!.equals("caption"))
{
if (t.isEndTag() && t.asEndTag().normalName()!.equals("caption")) {
let endTag: Token.EndTag = t.asEndTag()
let name: String? = endTag.normalName()
if (try name != nil && !tb.inTableScope(name!)) {
@ -1010,7 +995,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
return false
} else {
tb.generateImpliedEndTags()
if (!tb.currentElement()!.nodeName().equals("caption")){
if (!tb.currentElement()!.nodeName().equals("caption")) {
tb.error(self)
}
tb.popStackToClose("caption")
@ -1024,7 +1009,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
) {
tb.error(self)
let processed: Bool = try tb.processEndTag("caption")
if (processed){
if (processed) {
return try tb.process(t)
}
} else if (t.isEndTag() && StringUtil.inString(t.asEndTag().normalName()!,
@ -1038,14 +1023,12 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
case .InColumnGroup:
func anythingElse(_ t: Token, _ tb: TreeBuilder)throws->Bool {
let processed: Bool = try tb.processEndTag("colgroup")
if (processed){ // only ignored in frag case
if (processed) { // only ignored in frag case
return try tb.process(t)
}
return true
}
if (HtmlTreeBuilderState.isWhitespace(t)) {
try tb.insert(t.asCharacter())
return true
@ -1060,11 +1043,11 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
case .StartTag:
let startTag: Token.StartTag = t.asStartTag()
let name: String? = startTag.normalName()
if ("html".equals(name)){
if ("html".equals(name)) {
return try tb.process(t, .InBody)
}else if ("col".equals(name)){
} else if ("col".equals(name)) {
try tb.insertEmpty(startTag)
}else{
} else {
return try anythingElse(t, tb)
}
break
@ -1079,14 +1062,14 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
tb.pop()
tb.transition(.InTable)
}
} else{
} else {
return try anythingElse(t, tb)
}
break
case .EOF:
if ("html".equals(tb.currentElement()?.nodeName())){
if ("html".equals(tb.currentElement()?.nodeName())) {
return true // stop parsing; frag case
}else{
} else {
return try anythingElse(t, tb)
}
default:
@ -1095,7 +1078,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
return true
case .InTableBody:
@discardableResult
func exitTableBody(_ t:Token, _ tb: HtmlTreeBuilder)throws->Bool {
func exitTableBody(_ t: Token, _ tb: HtmlTreeBuilder)throws->Bool {
if (try !(tb.inTableScope("tbody") || tb.inTableScope("thead") || tb.inScope("tfoot"))) {
// frag case
tb.error(self)
@ -1110,7 +1093,6 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
return try tb.process(t, .InTable)
}
switch (t.type) {
case .StartTag:
let startTag: Token.StartTag = t.asStartTag()
@ -1125,7 +1107,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
return try tb.process(startTag)
} else if (StringUtil.inString(name, haystack: "caption", "col", "colgroup", "tbody", "tfoot", "thead")) {
return try exitTableBody(t, tb)
} else{
} else {
return try anythingElse(t, tb)
}
break
@ -1146,7 +1128,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
} else if (StringUtil.inString(name, haystack: "body", "caption", "col", "colgroup", "html", "td", "th", "tr")) {
tb.error(self)
return false
} else{
} else {
return try anythingElse(t, tb)
}
break
@ -1161,9 +1143,9 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
func handleMissingTr(_ t: Token, _ tb: TreeBuilder)throws->Bool {
let processed: Bool = try tb.processEndTag("tr")
if (processed){
if (processed) {
return try tb.process(t)
}else{
} else {
return false
}
}
@ -1219,14 +1201,13 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
}
func closeCell(_ tb: HtmlTreeBuilder)throws {
if (try tb.inTableScope("td")){
if (try tb.inTableScope("td")) {
try tb.processEndTag("td")
}else{
} else {
try tb.processEndTag("th") // only here if th or td in scope
}
}
if (t.isEndTag()) {
let endTag: Token.EndTag = t.asEndTag()
let name: String? = endTag.normalName()
@ -1238,7 +1219,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
return false
}
tb.generateImpliedEndTags()
if (!name!.equals(tb.currentElement()?.nodeName())){
if (!name!.equals(tb.currentElement()?.nodeName())) {
tb.error(self)
}
tb.popStackToClose(name!)
@ -1272,12 +1253,11 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
return true
case .InSelect:
func anythingElse(_ t: Token, _ tb: HtmlTreeBuilder)->Bool {
func anythingElse(_ t: Token, _ tb: HtmlTreeBuilder) -> Bool {
tb.error(self)
return false
}
switch (t.type) {
case .Char:
let c: Token.Char = t.asCharacter()
@ -1297,15 +1277,15 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
case .StartTag:
let start: Token.StartTag = t.asStartTag()
let name: String? = start.normalName()
if ("html".equals(name)){
if ("html".equals(name)) {
return try tb.process(start, .InBody)
}else if ("option".equals(name)) {
} else if ("option".equals(name)) {
try tb.processEndTag("option")
try tb.insert(start)
} else if ("optgroup".equals(name)) {
if ("option".equals(tb.currentElement()?.nodeName())){
if ("option".equals(tb.currentElement()?.nodeName())) {
try tb.processEndTag("option")
}else if ("optgroup".equals(tb.currentElement()?.nodeName())){
} else if ("optgroup".equals(tb.currentElement()?.nodeName())) {
try tb.processEndTag("optgroup")
}
try tb.insert(start)
@ -1314,7 +1294,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
return try tb.processEndTag("select")
} else if (StringUtil.inString(name, haystack: "input", "keygen", "textarea")) {
tb.error(self)
if (try !tb.inSelectScope("select")){
if (try !tb.inSelectScope("select")) {
return false // frag
}
try tb.processEndTag("select")
@ -1329,18 +1309,18 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
let end: Token.EndTag = t.asEndTag()
let name = end.normalName()
if ("optgroup".equals(name)) {
if ("option".equals(tb.currentElement()?.nodeName()) && tb.currentElement() != nil && tb.aboveOnStack(tb.currentElement()!) != nil && "optgroup".equals(tb.aboveOnStack(tb.currentElement()!)?.nodeName())){
if ("option".equals(tb.currentElement()?.nodeName()) && tb.currentElement() != nil && tb.aboveOnStack(tb.currentElement()!) != nil && "optgroup".equals(tb.aboveOnStack(tb.currentElement()!)?.nodeName())) {
try tb.processEndTag("option")
}
if ("optgroup".equals(tb.currentElement()?.nodeName())){
if ("optgroup".equals(tb.currentElement()?.nodeName())) {
tb.pop()
}else{
} else {
tb.error(self)
}
} else if ("option".equals(name)) {
if ("option".equals(tb.currentElement()?.nodeName())){
if ("option".equals(tb.currentElement()?.nodeName())) {
tb.pop()
}else{
} else {
tb.error(self)
}
} else if ("select".equals(name)) {
@ -1351,12 +1331,12 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
tb.popStackToClose(name!)
tb.resetInsertionMode()
}
} else{
} else {
return anythingElse(t, tb)
}
break
case .EOF:
if (!"html".equals(tb.currentElement()?.nodeName())){
if (!"html".equals(tb.currentElement()?.nodeName())) {
tb.error(self)
}
break
@ -1374,7 +1354,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
if (try t.asEndTag().normalName() != nil && tb.inTableScope(t.asEndTag().normalName()!)) {
try tb.processEndTag("select")
return try (tb.process(t))
} else{
} else {
return false
}
} else {
@ -1507,7 +1487,7 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
}
private static func isWhitespace(_ t: Token)->Bool {
private static func isWhitespace(_ t: Token) -> Bool {
if (t.isCharacter()) {
let data: String? = t.asCharacter().getData()
return isWhitespace(data)
@ -1515,11 +1495,11 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
return false
}
private static func isWhitespace(_ data: String?)->Bool {
private static func isWhitespace(_ data: String?) -> Bool {
// todo: self checks more than spec - "\t", "\n", "\f", "\r", " "
if let data = data{
if let data = data {
for c in data.characters {
if (!StringUtil.isWhitespace(c)){
if (!StringUtil.isWhitespace(c)) {
return false}
}
}
@ -1540,13 +1520,6 @@ enum HtmlTreeBuilderState: String, HtmlTreeBuilderStateProtocol
tb.transition(.Text)
}
// lists of tags to search through. A little harder to read here, but causes less GC than dynamic varargs.
// was contributing around 10% of parse GC load.
fileprivate final class Constants {

View File

@ -8,13 +8,12 @@
import Foundation
open class Node : Equatable, Hashable
{
private static let EMPTY_NODES : Array<Node> = Array<Node>()
var parentNode : Node?
var childNodes : Array <Node>
var attributes : Attributes?
var baseUri : String?
open class Node: Equatable, Hashable {
private static let EMPTY_NODES: Array<Node> = Array<Node>()
var parentNode: Node?
var childNodes: Array <Node>
var attributes: Attributes?
var baseUri: String?
/**
* Get the list index of this node in its node sibling list. I.e. if this is the first node
@ -22,7 +21,7 @@ open class Node : Equatable, Hashable
* @return position in node sibling list
* @see org.jsoup.nodes.Element#elementSiblingIndex()
*/
public private(set) var siblingIndex : Int = 0
public private(set) var siblingIndex: Int = 0
/**
Create a new Node.
@ -54,7 +53,7 @@ open class Node : Equatable, Hashable
Get the node name of this node. Use for debugging purposes and not logic switching (for that, use instanceof).
@return node name
*/
public func nodeName()->String {
public func nodeName() -> String {
preconditionFailure("This method must be overridden")
}
@ -74,19 +73,19 @@ open class Node : Equatable, Hashable
* @see #absUrl(String)
*/
open func attr(_ attributeKey: String)throws ->String {
let val : String = try attributes!.getIgnoreCase(key: attributeKey)
if (val.characters.count > 0){
let val: String = try attributes!.getIgnoreCase(key: attributeKey)
if (val.characters.count > 0) {
return val
}else if (attributeKey.lowercased().startsWith("abs:")){
} else if (attributeKey.lowercased().startsWith("abs:")) {
return try absUrl(attributeKey.substring("abs:".characters.count))
}else {return ""}
} else {return ""}
}
/**
* Get all of the element's attributes.
* @return attributes (which implements iterable, in same order as presented in original HTML).
*/
open func getAttributes()->Attributes? {
open func getAttributes() -> Attributes? {
return attributes
}
@ -98,7 +97,7 @@ open class Node : Equatable, Hashable
*/
@discardableResult
open func attr(_ attributeKey: String, _ attributeValue: String)throws->Node {
try attributes?.put(attributeKey , attributeValue)
try attributes?.put(attributeKey, attributeValue)
return self
}
@ -107,18 +106,18 @@ open class Node : Equatable, Hashable
* @param attributeKey The attribute key to check.
* @return true if the attribute exists, false if not.
*/
open func hasAttr(_ attributeKey: String)->Bool {
open func hasAttr(_ attributeKey: String) -> Bool {
guard let attributes = attributes else {
return false
}
if (attributeKey.startsWith("abs:")) {
let key : String = attributeKey.substring("abs:".characters.count)
do{
let key: String = attributeKey.substring("abs:".characters.count)
do {
let abs = try absUrl(key)
if (attributes.hasKeyIgnoreCase(key: key) && !"".equals(abs)){
if (attributes.hasKeyIgnoreCase(key: key) && !"".equals(abs)) {
return true
}
}catch{
} catch {
return false
}
@ -141,7 +140,7 @@ open class Node : Equatable, Hashable
Get the base URI of this node.
@return base URI
*/
open func getBaseUri()->String {
open func getBaseUri() -> String {
return baseUri!
}
@ -149,13 +148,10 @@ open class Node : Equatable, Hashable
Update the base URI of this node and all of its descendants.
@param baseUri base URI to set
*/
open func setBaseUri(_ baseUri: String)throws
{
class nodeVisitor : NodeVisitor
{
private let baseUri : String
init(_ baseUri: String)
{
open func setBaseUri(_ baseUri: String)throws {
class nodeVisitor: NodeVisitor {
private let baseUri: String
init(_ baseUri: String) {
self.baseUri = baseUri
}
@ -207,7 +203,7 @@ open class Node : Equatable, Hashable
@param index index of child node
@return the child node at this index. Throws a {@code IndexOutOfBoundsException} if the index is out of bounds.
*/
open func childNode(_ index: Int)->Node {
open func childNode(_ index: Int) -> Node {
return childNodes[index]
}
@ -225,10 +221,9 @@ open class Node : Equatable, Hashable
* nodes
* @return a deep copy of this node's children
*/
open func childNodesCopy()->Array<Node>{
open func childNodesCopy()->Array<Node> {
var children: Array<Node> = Array<Node>()
for node: Node in childNodes
{
for node: Node in childNodes {
children.append(node.copy() as! Node)
}
return children
@ -238,11 +233,11 @@ open class Node : Equatable, Hashable
* Get the number of child nodes that this node holds.
* @return the number of child nodes that this node holds.
*/
public func childNodeSize()->Int {
public func childNodeSize() -> Int {
return childNodes.count
}
final func childNodesAsArray()->[Node] {
final func childNodesAsArray() -> [Node] {
return childNodes as Array
}
@ -258,7 +253,7 @@ open class Node : Equatable, Hashable
Gets this node's parent node. Node overridable by extending classes, so useful if you really just need the Node type.
@return parent node or null if no parent.
*/
final func getParentNode()->Node? {
final func getParentNode() -> Node? {
return parentNode
}
@ -266,12 +261,12 @@ open class Node : Equatable, Hashable
* Gets the Document associated with this Node.
* @return the Document associated with this Node, or null if there is no such Document.
*/
open func ownerDocument()-> Document? {
if let this = self as? Document{
open func ownerDocument() -> Document? {
if let this = self as? Document {
return this
}else if (parentNode == nil){
} else if (parentNode == nil) {
return nil
}else{
} else {
return parentNode!.ownerDocument()
}
}
@ -306,7 +301,7 @@ open class Node : Equatable, Hashable
try Validate.notNull(obj: node)
try Validate.notNull(obj: parentNode)
try parentNode?.addChildren(siblingIndex,node)
try parentNode?.addChildren(siblingIndex, node)
return self
}
@ -333,20 +328,19 @@ open class Node : Equatable, Hashable
try Validate.notNull(obj: node)
try Validate.notNull(obj: parentNode)
try parentNode?.addChildren(siblingIndex+1,node)
try parentNode?.addChildren(siblingIndex+1, node)
return self
}
private func addSiblingHtml(_ index: Int, _ html: String)throws {
try Validate.notNull(obj: parentNode)
let context : Element? = parent() as? Element
let context: Element? = parent() as? Element
let nodes : Array<Node> = try Parser.parseFragment(html, context, getBaseUri())
try parentNode?.addChildren(index,nodes)
let nodes: Array<Node> = try Parser.parseFragment(html, context, getBaseUri())
try parentNode?.addChildren(index, nodes)
}
/**
* Insert the specified HTML into the DOM after this node (i.e. as a following sibling).
* @param html HTML to add after this node
@ -378,8 +372,8 @@ open class Node : Equatable, Hashable
try Validate.notNull(obj: html)
try Validate.notNull(obj: parentNode)
let context : Element? = parent() as? Element
let nodes : Array<Node> = try Parser.parseFragment(html, context, getBaseUri())
let context: Element? = parent() as? Element
let nodes: Array<Node> = try Parser.parseFragment(html, context, getBaseUri())
try parentNode?.addChildren(index, nodes)
}
@ -392,25 +386,23 @@ open class Node : Equatable, Hashable
open func wrap(_ html: String)throws->Node? {
try Validate.notEmpty(string: html)
let context : Element? = parent() as? Element
var wrapChildren : Array<Node> = try Parser.parseFragment(html, context, getBaseUri())
let wrapNode : Node? = wrapChildren.count > 0 ? wrapChildren[0] : nil
if (wrapNode == nil || !(((wrapNode as? Element) != nil))){ // nothing to wrap with; noop
let context: Element? = parent() as? Element
var wrapChildren: Array<Node> = try Parser.parseFragment(html, context, getBaseUri())
let wrapNode: Node? = wrapChildren.count > 0 ? wrapChildren[0] : nil
if (wrapNode == nil || !(((wrapNode as? Element) != nil))) { // nothing to wrap with; noop
return nil
}
let wrap : Element = wrapNode as! Element
let deepest : Element = getDeepChild(el: wrap)
let wrap: Element = wrapNode as! Element
let deepest: Element = getDeepChild(el: wrap)
try parentNode?.replaceChild(self, wrap)
wrapChildren = wrapChildren.filter { $0 != wrap}
try deepest.addChildren(self)
// remainder (unbalanced wrap, like <div></div><p></p> -- The <p> is remainder
if (wrapChildren.count > 0)
{
for i in 0..<wrapChildren.count
{
let remainder : Node = wrapChildren[i]
if (wrapChildren.count > 0) {
for i in 0..<wrapChildren.count {
let remainder: Node = wrapChildren[i]
try remainder.parentNode?.removeChild(remainder)
try wrap.appendChild(remainder)
}
@ -437,18 +429,18 @@ open class Node : Equatable, Hashable
open func unwrap()throws ->Node? {
try Validate.notNull(obj: parentNode)
let firstChild : Node? = childNodes.count > 0 ? childNodes[0] : nil
let firstChild: Node? = childNodes.count > 0 ? childNodes[0] : nil
try parentNode?.addChildren(siblingIndex, self.childNodesAsArray())
try self.remove()
return firstChild
}
private func getDeepChild(el: Element)->Element {
private func getDeepChild(el: Element) -> Element {
let children = el.children()
if (children.size() > 0){
if (children.size() > 0) {
return getDeepChild(el: children.get(0))
}else{
} else {
return el
}
}
@ -464,7 +456,7 @@ open class Node : Equatable, Hashable
}
public func setParentNode(_ parentNode: Node)throws {
if (self.parentNode != nil){
if (self.parentNode != nil) {
try self.parentNode?.removeChild(self)
}
self.parentNode = parentNode
@ -473,11 +465,11 @@ open class Node : Equatable, Hashable
public func replaceChild(_ out: Node, _ input: Node)throws {
try Validate.isTrue(val: out.parentNode === self)
try Validate.notNull(obj: input)
if (input.parentNode != nil){
if (input.parentNode != nil) {
try input.parentNode?.removeChild(input)
}
let index : Int = out.siblingIndex
let index: Int = out.siblingIndex
childNodes[index] = input
input.parentNode = self
input.setSiblingIndex(index)
@ -486,7 +478,7 @@ open class Node : Equatable, Hashable
public func removeChild(_ out: Node)throws {
try Validate.isTrue(val: out.parentNode === self)
let index : Int = out.siblingIndex
let index: Int = out.siblingIndex
childNodes.remove(at: index)
reindexChildren(index)
out.parentNode = nil
@ -499,8 +491,7 @@ open class Node : Equatable, Hashable
public func addChildren(_ children: [Node])throws {
//most used. short circuit addChildren(int), which hits reindex children and array copy
for child in children
{
for child in children {
try reparentChild(child)
ensureChildNodes()
childNodes.append(child)
@ -508,38 +499,35 @@ open class Node : Equatable, Hashable
}
}
public func addChildren(_ index: Int,_ children: Node...)throws {
try addChildren(index,children)
public func addChildren(_ index: Int, _ children: Node...)throws {
try addChildren(index, children)
}
public func addChildren(_ index: Int,_ children: [Node])throws {
public func addChildren(_ index: Int, _ children: [Node])throws {
ensureChildNodes()
for i in (0..<children.count).reversed()
{
let input : Node = children[i]
for i in (0..<children.count).reversed() {
let input: Node = children[i]
try reparentChild(input)
childNodes.insert(input, at: index)
reindexChildren(index)
}
}
public func ensureChildNodes()
{
public func ensureChildNodes() {
// if (childNodes === Node.EMPTY_NODES) {
// childNodes = Array<Node>()
// }
}
public func reparentChild(_ child: Node)throws {
if (child.parentNode != nil){
if (child.parentNode != nil) {
try child.parentNode?.removeChild(child)
}
try child.setParentNode(self)
}
private func reindexChildren(_ start: Int) {
for i in start..<childNodes.count
{
for i in start..<childNodes.count {
childNodes[i].setSiblingIndex(i)
}
}
@ -550,15 +538,14 @@ open class Node : Equatable, Hashable
@return node siblings. If the node has no parent, returns an empty list.
*/
open func siblingNodes()->Array<Node> {
if (parentNode == nil){
if (parentNode == nil) {
return Array<Node>()
}
let nodes : Array<Node> = parentNode!.childNodes
var siblings : Array<Node> = Array<Node>()
for node in nodes
{
if (node !== self){
let nodes: Array<Node> = parentNode!.childNodes
var siblings: Array<Node> = Array<Node>()
for node in nodes {
if (node !== self) {
siblings.append(node)
}
}
@ -566,21 +553,20 @@ open class Node : Equatable, Hashable
return siblings
}
/**
Get this node's next sibling.
@return next sibling, or null if this is the last sibling
*/
open func nextSibling()->Node? {
if (parentNode == nil){
open func nextSibling() -> Node? {
if (parentNode == nil) {
return nil // root
}
let siblings : Array<Node> = parentNode!.childNodes
let index : Int = siblingIndex+1
if (siblings.count > index){
let siblings: Array<Node> = parentNode!.childNodes
let index: Int = siblingIndex+1
if (siblings.count > index) {
return siblings[index]
}else{
} else {
return nil
}
}
@ -589,20 +575,18 @@ open class Node : Equatable, Hashable
Get this node's previous sibling.
@return the previous sibling, or null if this is the first sibling
*/
open func previousSibling()->Node? {
if (parentNode == nil){
open func previousSibling() -> Node? {
if (parentNode == nil) {
return nil // root
}
if (siblingIndex > 0){
if (siblingIndex > 0) {
return parentNode?.childNodes[siblingIndex-1]
}else{
} else {
return nil
}
}
public func setSiblingIndex(_ siblingIndex: Int) {
self.siblingIndex = siblingIndex
}
@ -614,7 +598,7 @@ open class Node : Equatable, Hashable
*/
@discardableResult
open func traverse(_ nodeVisitor: NodeVisitor)throws->Node {
let traversor : NodeTraversor = NodeTraversor(nodeVisitor)
let traversor: NodeTraversor = NodeTraversor(nodeVisitor)
try traversor.traverse(self)
return self
}
@ -624,7 +608,7 @@ open class Node : Equatable, Hashable
@return HTML
*/
open func outerHtml()throws->String {
let accum : StringBuilder = StringBuilder(128)
let accum: StringBuilder = StringBuilder(128)
try outerHtml(accum)
return accum.toString()
}
@ -634,7 +618,7 @@ open class Node : Equatable, Hashable
}
// if this node has no document (or parent), retrieve the default output settings
func getOutputSettings()-> OutputSettings {
func getOutputSettings() -> OutputSettings {
return ownerDocument() != nil ? ownerDocument()!.outputSettings() : (Document("")).outputSettings()
}
@ -643,17 +627,14 @@ open class Node : Equatable, Hashable
@param accum accumulator to place HTML into
@throws IOException if appending to the given accumulator fails.
*/
func outerHtmlHead(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) throws
{
func outerHtmlHead(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) throws {
preconditionFailure("This method must be overridden")
}
func outerHtmlTail(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) throws
{
func outerHtmlTail(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) throws {
preconditionFailure("This method must be overridden")
}
/**
* Write this node and its children to the given {@link Appendable}.
*
@ -680,7 +661,7 @@ open class Node : Equatable, Hashable
* @see Node#hasSameValue(Object) to compare nodes by their value
*/
open func equals(_ o: Node)->Bool {
open func equals(_ o: Node) -> Bool {
// implemented just so that javadoc is clear this is an identity test
return self === o
}
@ -692,8 +673,8 @@ open class Node : Equatable, Hashable
* @return true if the content of this node is the same as the other
*/
open func hasSameValue(_ o : Node)throws->Bool {
if (self === o){return true}
open func hasSameValue(_ o: Node)throws->Bool {
if (self === o) {return true}
// if (type(of:self) != type(of: o))
// {
// return false
@ -710,31 +691,27 @@ open class Node : Equatable, Hashable
* The cloned node may be adopted into another Document or node structure using {@link Element#appendChild(Node)}.
* @return stand-alone cloned node
*/
public func copy(with zone: NSZone? = nil) -> Any
{
public func copy(with zone: NSZone? = nil) -> Any {
return copy(clone: Node())
}
public func copy(parent: Node?)->Node
{
public func copy(parent: Node?) -> Node {
let clone = Node()
return copy(clone: clone,parent: parent)
return copy(clone: clone, parent: parent)
}
public func copy(clone: Node)->Node
{
let thisClone : Node = copy(clone: clone, parent: nil) // splits for orphan
public func copy(clone: Node) -> Node {
let thisClone: Node = copy(clone: clone, parent: nil) // splits for orphan
// Queue up nodes that need their children cloned (BFS).
var nodesToProcess : Array<Node> = Array<Node>()
var nodesToProcess: Array<Node> = Array<Node>()
nodesToProcess.append(thisClone)
while (!nodesToProcess.isEmpty) {
let currParent : Node = nodesToProcess.removeFirst()
let currParent: Node = nodesToProcess.removeFirst()
for i in 0..<currParent.childNodes.count
{
let childClone : Node = currParent.childNodes[i].copy(parent:currParent)
for i in 0..<currParent.childNodes.count {
let childClone: Node = currParent.childNodes[i].copy(parent:currParent)
currParent.childNodes[i] = childClone
nodesToProcess.append(childClone)
}
@ -746,48 +723,41 @@ open class Node : Equatable, Hashable
* Return a clone of the node using the given parent (which can be null).
* Not a deep copy of children.
*/
public func copy(clone: Node, parent: Node?)->Node
{
public func copy(clone: Node, parent: Node?) -> Node {
clone.parentNode = parent // can be null, to create an orphan split
clone.siblingIndex = parent == nil ? 0 : siblingIndex
clone.attributes = attributes != nil ? attributes?.clone() : nil
clone.baseUri = baseUri
clone.childNodes = Array<Node>()
for child in childNodes{
for child in childNodes {
clone.childNodes.append(child)
}
return clone
}
private class OuterHtmlVisitor : NodeVisitor {
private var accum : StringBuilder
private var out : OutputSettings
private class OuterHtmlVisitor: NodeVisitor {
private var accum: StringBuilder
private var out: OutputSettings
init(_ accum: StringBuilder, _ out: OutputSettings) {
self.accum = accum
self.out = out
}
open func head(_ node: Node, _ depth: Int)throws{
open func head(_ node: Node, _ depth: Int)throws {
try node.outerHtmlHead(accum, depth, out)
}
open func tail(_ node: Node, _ depth: Int)throws {
if (!(node.nodeName() == "#text"))
{ // saves a void hit.
if (!(node.nodeName() == "#text")) { // saves a void hit.
try node.outerHtmlTail(accum, depth, out)
}
}
}
/// Returns a Boolean value indicating whether two values are equal.
///
/// Equality is the inverse of inequality. For any values `a` and `b`,
@ -796,46 +766,40 @@ open class Node : Equatable, Hashable
/// - Parameters:
/// - lhs: A value to compare.
/// - rhs: Another value to compare.
public static func ==(lhs: Node, rhs: Node) -> Bool{
public static func ==(lhs: Node, rhs: Node) -> Bool {
return lhs === rhs
}
/// The hash value.
///
/// Hash values are not guaranteed to be equal across different executions of
/// your program. Do not save hash values to use during a future execution.
public var hashValue: Int {
var result : Int = description.hashValue
var result: Int = description.hashValue
result = Int.addWithOverflow(Int.multiplyWithOverflow(31, result).0, baseUri != nil ? baseUri!.hashValue : 31).0
return result
}
}
extension Node : CustomStringConvertible
{
extension Node : CustomStringConvertible {
public var description: String {
do{
do {
return try toString()
}catch{
} catch {
}
return ""
}
}
extension Node : CustomDebugStringConvertible
{
extension Node : CustomDebugStringConvertible {
public var debugDescription: String {
do{
do {
return try String(describing: type(of: self)) + " " + toString()
}catch{
} catch {
}
return String(describing: type(of: self))
}
}

View File

@ -8,9 +8,8 @@
import Foundation
class NodeTraversor
{
private let visitor : NodeVisitor
class NodeTraversor {
private let visitor: NodeVisitor
/**
* Create a new traversor.
@ -25,8 +24,8 @@ class NodeTraversor
* @param root the root node point to traverse.
*/
open func traverse(_ root: Node?)throws {
var node : Node? = root
var depth : Int = 0
var node: Node? = root
var depth: Int = 0
while (node != nil) {
try visitor.head(node!, depth)
@ -40,7 +39,7 @@ class NodeTraversor
depth-=1
}
try visitor.tail(node!, depth)
if (node === root){
if (node === root) {
break
}
node = node!.nextSibling()
@ -48,5 +47,4 @@ class NodeTraversor
}
}
}

View File

@ -8,7 +8,7 @@
import Foundation
public class OrderedDictionary<Key: Hashable, Value: Equatable>: MutableCollection ,Hashable {
public class OrderedDictionary<Key: Hashable, Value: Equatable>: MutableCollection, Hashable {
/// Returns the position immediately after the given index.
///
@ -32,7 +32,7 @@ public class OrderedDictionary<Key: Hashable, Value: Equatable>: MutableCollecti
// ======================================================= //
public init() {}
public init(count:Int) {}
public init(count: Int) {}
public init(elements: [Element]) {
for (key, value) in elements {
@ -44,16 +44,14 @@ public class OrderedDictionary<Key: Hashable, Value: Equatable>: MutableCollecti
return copy(with: nil)
}
public func mutableCopy(with zone: NSZone? = nil) -> Any
{
public func mutableCopy(with zone: NSZone? = nil) -> Any {
return copy()
}
public func copy(with zone: NSZone? = nil) -> Any {
let copy = OrderedDictionary<Key,Value>()
let copy = OrderedDictionary<Key, Value>()
//let copy = type(of:self).init()
for element in orderedKeys
{
for element in orderedKeys {
copy.put(value: valueForKey(key: element)!, forKey: element)
}
return copy
@ -113,7 +111,6 @@ public class OrderedDictionary<Key: Hashable, Value: Equatable>: MutableCollecti
return 0
}
public func hashCode() -> Int {
return hashValue
}
@ -136,15 +133,12 @@ public class OrderedDictionary<Key: Hashable, Value: Equatable>: MutableCollecti
}
}
public func put(value: Value, forKey key: Key) {
self[key] = value
}
public func putAll(all:OrderedDictionary<Key, Value>)
{
for i in all.orderedKeys
{
public func putAll(all: OrderedDictionary<Key, Value>) {
for i in all.orderedKeys {
put(value:all[i]!, forKey: i)
}
}
@ -175,7 +169,6 @@ public class OrderedDictionary<Key: Hashable, Value: Equatable>: MutableCollecti
_keysToValues.removeAll(keepingCapacity: keepCapacity)
}
// ======================================================= //
// MARK: - Managing Content Using Indexes
// ======================================================= //
@ -370,8 +363,7 @@ extension OrderedDictionary: CustomStringConvertible, CustomDebugStringConvertib
}
extension OrderedDictionary: Equatable
{
extension OrderedDictionary: Equatable {
/// Returns a Boolean value indicating whether two values are equal.
///
/// Equality is the inverse of inequality. For any values `a` and `b`,
@ -381,12 +373,11 @@ extension OrderedDictionary: Equatable
/// - lhs: A value to compare.
/// - rhs: Another value to compare.
public static func ==(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
if(lhs.count != rhs.count){return false}
if(lhs.count != rhs.count) {return false}
return (lhs._orderedKeys == rhs._orderedKeys) && (lhs._keysToValues == rhs._keysToValues)
}
}
//public func == <Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
// return lhs._orderedKeys == rhs._orderedKeys && lhs._keysToValues == rhs._keysToValues
//}

View File

@ -13,14 +13,13 @@ public class OrderedSet<T: Hashable> {
fileprivate var contents = [T: Index]() // Needs to have a value of Index instead of Void for fast removals
fileprivate var sequencedContents = Array<UnsafeMutablePointer<T>>()
/**
Inititalizes an empty ordered set.
- returns: An empty ordered set.
*/
public init() { }
deinit{
deinit {
removeAllObjects()
}

View File

@ -12,8 +12,8 @@ import Foundation
* A Parse Error records an error in the input HTML that occurs in either the tokenisation or the tree building phase.
*/
open class ParseError {
private let pos : Int
private let errorMsg : String
private let pos: Int
private let errorMsg: String
init(_ pos: Int, _ errorMsg: String) {
self.pos = pos
@ -24,7 +24,7 @@ open class ParseError {
* Retrieve the error message.
* @return the error message.
*/
open func getErrorMessage()->String {
open func getErrorMessage() -> String {
return errorMsg
}
@ -32,12 +32,11 @@ open class ParseError {
* Retrieves the offset of the error.
* @return error offset within input
*/
open func getPosition()->Int {
open func getPosition() -> Int {
return pos
}
open func toString()->String {
open func toString() -> String {
return "\(pos): " + errorMsg
}
}

View File

@ -8,12 +8,11 @@
import Foundation
public class ParseErrorList
{
private static let INITIAL_CAPACITY : Int = 16
private let maxSize : Int
private let initialCapacity : Int
private var array : Array<ParseError?> = Array<ParseError>()
public class ParseErrorList {
private static let INITIAL_CAPACITY: Int = 16
private let maxSize: Int
private let initialCapacity: Int
private var array: Array<ParseError?> = Array<ParseError>()
init(_ initialCapacity: Int, _ maxSize: Int) {
self.maxSize = maxSize
@ -21,19 +20,19 @@ public class ParseErrorList
array = Array(repeating: nil, count: maxSize)
}
func canAddError()->Bool {
func canAddError() -> Bool {
return array.count < maxSize
}
func getMaxSize()->Int {
func getMaxSize() -> Int {
return maxSize
}
static func noTracking()->ParseErrorList {
static func noTracking() -> ParseErrorList {
return ParseErrorList(0, 0)
}
static func tracking(_ maxSize: Int)->ParseErrorList {
static func tracking(_ maxSize: Int) -> ParseErrorList {
return ParseErrorList(INITIAL_CAPACITY, maxSize)
}
@ -46,7 +45,7 @@ public class ParseErrorList
array.append(e)
}
open func add(_ index: Int, _ element: ParseError){
open func add(_ index: Int, _ element: ParseError) {
array.insert(element, at: index)
}

View File

@ -8,21 +8,18 @@
import Foundation
open class ParseSettings
{
open class ParseSettings {
/**
* HTML default settings: both tag and attribute names are lower-cased during parsing.
*/
public static let htmlDefault : ParseSettings = ParseSettings(false, false)
public static let htmlDefault: ParseSettings = ParseSettings(false, false)
/**
* Preserve both tag and attribute case.
*/
public static let preserveCase : ParseSettings = ParseSettings(true, true)
private let preserveTagCase : Bool
private let preserveAttributeCase : Bool
public static let preserveCase: ParseSettings = ParseSettings(true, true)
private let preserveTagCase: Bool
private let preserveAttributeCase: Bool
/**
* Define parse settings.
@ -34,17 +31,17 @@ open class ParseSettings
preserveAttributeCase = attribute
}
open func normalizeTag(_ name: String)->String {
open func normalizeTag(_ name: String) -> String {
var name = name.trim()
if (!preserveTagCase){
if (!preserveTagCase) {
name = name.lowercased()
}
return name
}
open func normalizeAttribute(_ name: String)->String {
open func normalizeAttribute(_ name: String) -> String {
var name = name.trim()
if (!preserveAttributeCase){
if (!preserveAttributeCase) {
name = name.lowercased()
}
return name
@ -52,13 +49,11 @@ open class ParseSettings
open func normalizeAttributes(_ attributes: Attributes)throws ->Attributes {
if (!preserveAttributeCase) {
for attr in attributes.iterator()
{
for attr in attributes.iterator() {
try attr.setKey(key: attr.getKey().lowercased())
}
}
return attributes
}
}

View File

@ -8,18 +8,16 @@
import Foundation
/**
* Parses HTML into a {@link org.jsoup.nodes.Document}. Generally best to use one of the more convenient parse methods
* in {@link org.jsoup.Jsoup}.
*/
public class Parser
{
public class Parser {
private static let DEFAULT_MAX_ERRORS: Int = 0 // by default, error tracking is disabled.
private var _treeBuilder: TreeBuilder
private var _maxErrors: Int = DEFAULT_MAX_ERRORS
private var _errors: ParseErrorList = ParseErrorList(16,16)
private var _errors: ParseErrorList = ParseErrorList(16, 16)
private var _settings: ParseSettings
/**
@ -41,7 +39,7 @@ public class Parser
* Get the TreeBuilder currently in use.
* @return current TreeBuilder.
*/
public func getTreeBuilder()->TreeBuilder {
public func getTreeBuilder() -> TreeBuilder {
return _treeBuilder
}
@ -51,7 +49,7 @@ public class Parser
* @return this, for chaining
*/
@discardableResult
public func setTreeBuilder(_ treeBuilder: TreeBuilder)->Parser {
public func setTreeBuilder(_ treeBuilder: TreeBuilder) -> Parser {
self._treeBuilder = treeBuilder
return self
}
@ -60,7 +58,7 @@ public class Parser
* Check if parse error tracking is enabled.
* @return current track error state.
*/
public func isTrackErrors()->Bool {
public func isTrackErrors() -> Bool {
return _maxErrors > 0
}
@ -70,7 +68,7 @@ public class Parser
* @return this, for chaining
*/
@discardableResult
public func setTrackErrors(_ maxErrors: Int)->Parser {
public func setTrackErrors(_ maxErrors: Int) -> Parser {
self._maxErrors = maxErrors
return self
}
@ -79,17 +77,17 @@ public class Parser
* Retrieve the parse errors, if any, from the last parse.
* @return list of parse errors, up to the size of the maximum errors tracked.
*/
public func getErrors()->ParseErrorList {
public func getErrors() -> ParseErrorList {
return _errors
}
@discardableResult
public func settings(_ settings: ParseSettings)->Parser {
public func settings(_ settings: ParseSettings) -> Parser {
self._settings = settings
return self
}
public func settings()->ParseSettings {
public func settings() -> ParseSettings {
return _settings
}
@ -144,15 +142,13 @@ public class Parser
*/
public static func parseBodyFragment(_ bodyHtml: String, _ baseUri: String)throws->Document {
let doc: Document = Document.createShell(baseUri)
if let body: Element = doc.body()
{
if let body: Element = doc.body() {
let nodeList: Array<Node> = try parseFragment(bodyHtml, body, baseUri)
//var nodes: [Node] = nodeList.toArray(Node[nodeList.size()]) // the node list gets modified when re-parented
for i in (nodeList.count - 1)...1
{
for i in (nodeList.count - 1)...1 {
try nodeList[i].remove()
}
for node:Node in nodeList {
for node: Node in nodeList {
try body.appendChild(node)
}
}
@ -188,7 +184,7 @@ public class Parser
* based on a knowledge of the semantics of the incoming tags.
* @return a new HTML parser.
*/
public static func htmlParser()->Parser {
public static func htmlParser() -> Parser {
return Parser(HtmlTreeBuilder())
}
@ -197,7 +193,7 @@ public class Parser
* rather creates a simple tree directly from the input.
* @return a new simple XML parser.
*/
public static func xmlParser()->Parser {
public static func xmlParser() -> Parser {
return Parser(XmlTreeBuilder())
}
}

View File

@ -24,17 +24,14 @@ public struct Pattern {
self.pattern = pattern
}
static public func compile(_ s: String)->Pattern
{
static public func compile(_ s: String) -> Pattern {
return Pattern(s)
}
static public func compile(_ s: String, _ op: Int)->Pattern
{
static public func compile(_ s: String, _ op: Int) -> Pattern {
return Pattern(s)
}
func validate()throws
{
func validate()throws {
_ = try NCRegularExpression(pattern: self.pattern, options:[])
}
@ -42,55 +39,49 @@ public struct Pattern {
do {
let regex = try NCRegularExpression(pattern: self.pattern, options:[])
let nsString = NSString(string: text)
let results = regex.matches(in: text,options:[], range: NSRange(location: 0, length: nsString.length))
let results = regex.matches(in: text, options:[], range: NSRange(location: 0, length: nsString.length))
return Matcher(results,text)
return Matcher(results, text)
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return Matcher([],text)
return Matcher([], text)
}
}
public func toString()->String{
public func toString() -> String {
return pattern
}
}
public class Matcher
{
let matches :[NCTextCheckingResult]
let string : String
var index : Int = -1
public class Matcher {
let matches: [NCTextCheckingResult]
let string: String
var index: Int = -1
public var count : Int { return matches.count}
public var count: Int { return matches.count}
init(_ m:[NCTextCheckingResult],_ s: String)
{
init(_ m: [NCTextCheckingResult], _ s: String) {
matches = m
string = s
}
@discardableResult
public func find() -> Bool
{
public func find() -> Bool {
index += 1
if(index < matches.count)
{
if(index < matches.count) {
return true
}
return false
}
public func group(_ i: Int) -> String?
{
public func group(_ i: Int) -> String? {
let b = matches[index]
let c = b.range(at:i)
if(c.location == NSNotFound) {return nil}
let result = string.substring(c.location,c.length)
let result = string.substring(c.location, c.length)
return result
}
public func group() -> String?
{
public func group() -> String? {
return group(0)
}
}

View File

@ -12,8 +12,8 @@ import Foundation
* Parses a CSS selector into an Evaluator tree.
*/
public class QueryParser {
private static let combinators : [String] = [",", ">", "+", "~", " "]
private static let AttributeEvals : [String] = ["=", "!=", "^=", "$=", "*=", "~="]
private static let combinators: [String] = [",", ">", "+", "~", " "]
private static let AttributeEvals: [String] = ["=", "!=", "^=", "$=", "*=", "~="]
private var tq: TokenQueue
private var query: String
@ -65,7 +65,7 @@ public class QueryParser {
}
}
if (evals.count == 1){
if (evals.count == 1) {
return evals[0]
}
return CombiningEvaluator.And(evals)
@ -88,24 +88,15 @@ public class QueryParser {
currentEval = (currentEval as! CombiningEvaluator.Or).rightMostEvaluator()
replaceRightMost = true
}
}
else {
} else {
currentEval = CombiningEvaluator.And(evals)
rootEval = currentEval
}
evals.removeAll()
// for most combinators: change the current eval into an AND of the current eval and the new eval
if (combinator == ">")
{currentEval = CombiningEvaluator.And(newEval, StructuralEvaluator.ImmediateParent(currentEval!))}
else if (combinator == " ")
{currentEval = CombiningEvaluator.And(newEval, StructuralEvaluator.Parent(currentEval!))}
else if (combinator == "+")
{currentEval = CombiningEvaluator.And(newEval, StructuralEvaluator.ImmediatePreviousSibling(currentEval!))}
else if (combinator == "~")
{currentEval = CombiningEvaluator.And(newEval, StructuralEvaluator.PreviousSibling(currentEval!))}
else if (combinator == ",") { // group or.
let or : CombiningEvaluator.Or
if (combinator == ">") {currentEval = CombiningEvaluator.And(newEval, StructuralEvaluator.ImmediateParent(currentEval!))} else if (combinator == " ") {currentEval = CombiningEvaluator.And(newEval, StructuralEvaluator.Parent(currentEval!))} else if (combinator == "+") {currentEval = CombiningEvaluator.And(newEval, StructuralEvaluator.ImmediatePreviousSibling(currentEval!))} else if (combinator == "~") {currentEval = CombiningEvaluator.And(newEval, StructuralEvaluator.PreviousSibling(currentEval!))} else if (combinator == ",") { // group or.
let or: CombiningEvaluator.Or
if ((currentEval as? CombiningEvaluator.Or) != nil) {
or = currentEval as! CombiningEvaluator.Or
or.add(newEval)
@ -115,33 +106,28 @@ public class QueryParser {
or.add(newEval)
}
currentEval = or
}
else{
} else {
throw Exception.Error(type: ExceptionType.SelectorParseException, Message: "Unknown combinator: \(String(combinator))")
}
if (replaceRightMost)
{
if (replaceRightMost) {
(rootEval as! CombiningEvaluator.Or).replaceRightMostEvaluator(currentEval!)
}
else {
} else {
rootEval = currentEval
}
evals.append(rootEval!)
}
private func consumeSubQuery()->String {
let sq : StringBuilder = StringBuilder()
private func consumeSubQuery() -> String {
let sq: StringBuilder = StringBuilder()
while (!tq.isEmpty()) {
if (tq.matches("(")){
if (tq.matches("(")) {
sq.append("(").append(tq.chompBalanced("(", ")")).append(")")
}else if (tq.matches("[")){
} else if (tq.matches("[")) {
sq.append("[").append(tq.chompBalanced("[", "]")).append("]")
}else if (tq.matchesAny(QueryParser.combinators)){
} else if (tq.matchesAny(QueryParser.combinators)) {
break
}else{
} else {
sq.append(tq.consume())
}
}
@ -149,61 +135,10 @@ public class QueryParser {
}
private func findElements()throws {
if (tq.matchChomp("#"))
{
if (tq.matchChomp("#")) {
try byId()
}else if (tq.matchChomp("."))
{
try byClass()}
else if (tq.matchesWord() || tq.matches("*|"))
{try byTag()}
else if (tq.matches("["))
{try byAttribute()}
else if (tq.matchChomp("*"))
{ allElements()}
else if (tq.matchChomp(":lt("))
{try indexLessThan()}
else if (tq.matchChomp(":gt("))
{try indexGreaterThan()}
else if (tq.matchChomp(":eq("))
{try indexEquals()}
else if (tq.matches(":has("))
{try has()}
else if (tq.matches(":contains("))
{try contains(false)}
else if (tq.matches(":containsOwn("))
{try contains(true)}
else if (tq.matches(":matches("))
{try matches(false)}
else if (tq.matches(":matchesOwn("))
{try matches(true)}
else if (tq.matches(":not("))
{try not()}
else if (tq.matchChomp(":nth-child("))
{try cssNthChild(false, false)}
else if (tq.matchChomp(":nth-last-child("))
{try cssNthChild(true, false)}
else if (tq.matchChomp(":nth-of-type("))
{try cssNthChild(false, true)}
else if (tq.matchChomp(":nth-last-of-type("))
{try cssNthChild(true, true)}
else if (tq.matchChomp(":first-child"))
{evals.append(Evaluator.IsFirstChild())}
else if (tq.matchChomp(":last-child"))
{evals.append(Evaluator.IsLastChild())}
else if (tq.matchChomp(":first-of-type"))
{evals.append(Evaluator.IsFirstOfType())}
else if (tq.matchChomp(":last-of-type"))
{evals.append(Evaluator.IsLastOfType())}
else if (tq.matchChomp(":only-child"))
{evals.append(Evaluator.IsOnlyChild())}
else if (tq.matchChomp(":only-of-type"))
{evals.append(Evaluator.IsOnlyOfType())}
else if (tq.matchChomp(":empty"))
{evals.append(Evaluator.IsEmpty())}
else if (tq.matchChomp(":root"))
{evals.append(Evaluator.IsRoot())}
else // unhandled
} else if (tq.matchChomp(".")) {
try byClass()} else if (tq.matchesWord() || tq.matches("*|")) {try byTag()} else if (tq.matches("[")) {try byAttribute()} else if (tq.matchChomp("*")) { allElements()} else if (tq.matchChomp(":lt(")) {try indexLessThan()} else if (tq.matchChomp(":gt(")) {try indexGreaterThan()} else if (tq.matchChomp(":eq(")) {try indexEquals()} else if (tq.matches(":has(")) {try has()} else if (tq.matches(":contains(")) {try contains(false)} else if (tq.matches(":containsOwn(")) {try contains(true)} else if (tq.matches(":matches(")) {try matches(false)} else if (tq.matches(":matchesOwn(")) {try matches(true)} else if (tq.matches(":not(")) {try not()} else if (tq.matchChomp(":nth-child(")) {try cssNthChild(false, false)} else if (tq.matchChomp(":nth-last-child(")) {try cssNthChild(true, false)} else if (tq.matchChomp(":nth-of-type(")) {try cssNthChild(false, true)} else if (tq.matchChomp(":nth-last-of-type(")) {try cssNthChild(true, true)} else if (tq.matchChomp(":first-child")) {evals.append(Evaluator.IsFirstChild())} else if (tq.matchChomp(":last-child")) {evals.append(Evaluator.IsLastChild())} else if (tq.matchChomp(":first-of-type")) {evals.append(Evaluator.IsFirstOfType())} else if (tq.matchChomp(":last-of-type")) {evals.append(Evaluator.IsLastOfType())} else if (tq.matchChomp(":only-child")) {evals.append(Evaluator.IsOnlyChild())} else if (tq.matchChomp(":only-of-type")) {evals.append(Evaluator.IsOnlyOfType())} else if (tq.matchChomp(":empty")) {evals.append(Evaluator.IsEmpty())} else if (tq.matchChomp(":root")) {evals.append(Evaluator.IsRoot())} else // unhandled
{
throw Exception.Error(type: ExceptionType.SelectorParseException, Message:"Could not parse query \(query): unexpected token at \(tq.remainder())")
}
@ -234,7 +169,7 @@ public class QueryParser {
Evaluator.TagEndsWith(tagName.replacingOccurrences(of: "*|", with: ":").trim().lowercased())))
} else {
// namespaces: if element name is "abc:def", selector must be "abc|def", so flip:
if (tagName.contains("|")){
if (tagName.contains("|")) {
tagName = tagName.replacingOccurrences(of: "|", with: ":")
}
@ -249,35 +184,25 @@ public class QueryParser {
cq.consumeWhitespace()
if (cq.isEmpty()) {
if (key.startsWith("^")){
if (key.startsWith("^")) {
evals.append(try Evaluator.AttributeStarting(key.substring(1)))
}else{
} else {
evals.append(Evaluator.Attribute(key))
}
} else {
if (cq.matchChomp("=")){
if (cq.matchChomp("=")) {
evals.append(try Evaluator.AttributeWithValue(key, cq.remainder()))
}
else if (cq.matchChomp("!=")){
} else if (cq.matchChomp("!=")) {
evals.append(try Evaluator.AttributeWithValueNot(key, cq.remainder()))
}
else if (cq.matchChomp("^=")){
} else if (cq.matchChomp("^=")) {
evals.append(try Evaluator.AttributeWithValueStarting(key, cq.remainder()))
}
else if (cq.matchChomp("$=")){
} else if (cq.matchChomp("$=")) {
evals.append(try Evaluator.AttributeWithValueEnding(key, cq.remainder()))
}
else if (cq.matchChomp("*=")){
} else if (cq.matchChomp("*=")) {
evals.append(try Evaluator.AttributeWithValueContaining(key, cq.remainder()))
}
else if (cq.matchChomp("~=")){
} else if (cq.matchChomp("~=")) {
evals.append( Evaluator.AttributeWithValueMatching(key, Pattern.compile(cq.remainder())))
}else{
} else {
throw Exception.Error(type: ExceptionType.SelectorParseException, Message:"Could not parse attribute query '\(query)': unexpected token at '\(cq.remainder())'")
}
}
@ -327,26 +252,21 @@ public class QueryParser {
} else {
throw Exception.Error(type: ExceptionType.SelectorParseException, Message:"Could not parse nth-index '\(argS)': unexpected format")
}
if (ofType){
if (backwards){
if (ofType) {
if (backwards) {
evals.append(Evaluator.IsNthLastOfType(a, b))
}else{
} else {
evals.append(Evaluator.IsNthOfType(a, b))
}
}else {
if (backwards){
} else {
if (backwards) {
evals.append(Evaluator.IsNthLastChild(a, b))
}else{
} else {
evals.append(Evaluator.IsNthChild(a, b))
}
}
}
private func consumeIndex()throws->Int {
let indexS: String = tq.chompTo(")").trim()
try Validate.isTrue(val: StringUtil.isNumeric(indexS), msg: "Index must be numeric")
@ -366,9 +286,9 @@ public class QueryParser {
try tq.consume(own ? ":containsOwn" : ":contains")
let searchText: String = TokenQueue.unescape(tq.chompBalanced("(", ")"))
try Validate.notEmpty(string: searchText, msg: ":contains(text) query must not be empty")
if (own){
if (own) {
evals.append(Evaluator.ContainsOwnText(searchText))
}else{
} else {
evals.append(Evaluator.ContainsText(searchText))
}
}
@ -379,9 +299,9 @@ public class QueryParser {
let regex: String = tq.chompBalanced("(", ")") // don't unescape, as regex bits will be escaped
try Validate.notEmpty(string: regex, msg: ":matches(regex) query must not be empty")
if (own){
if (own) {
evals.append(Evaluator.MatchesOwn(Pattern.compile(regex)))
}else{
} else {
evals.append(Evaluator.Matches(Pattern.compile(regex)))
}
}

View File

@ -73,8 +73,8 @@ import Foundation
* @see Element#select(String)
*/
open class Selector {
private let evaluator : Evaluator
private let root : Element
private let evaluator: Evaluator
private let root: Element
private init(_ query: String, _ root: Element)throws {
let query = query.trim()
@ -127,11 +127,9 @@ open class Selector {
var seenElements: Array<Element> = Array<Element>()
// dedupe elements by identity, not equality
for root: Element in roots
{
for root: Element in roots {
let found: Elements = try select(evaluator, root)
for el: Element in found.array()
{
for el: Element in found.array() {
if (!seenElements.contains(el)) {
elements.append(el)
seenElements.append(el)
@ -146,7 +144,7 @@ open class Selector {
}
// exclude set. package open so that Elements can implement .not() selector.
static func filterOut(_ elements: Array<Element>, _ outs: Array<Element>)->Elements {
static func filterOut(_ elements: Array<Element>, _ outs: Array<Element>) -> Elements {
let output: Elements = Elements()
for el: Element in elements {
var found: Bool = false
@ -156,7 +154,7 @@ open class Selector {
break
}
}
if (!found){
if (!found) {
output.add(el)
}
}

View File

@ -8,7 +8,6 @@
import Foundation
public class SimpleDictionary<KeyType: Hashable, ValueType> {
public typealias DictionaryType = [KeyType: ValueType]
@ -25,17 +24,15 @@ public class SimpleDictionary<KeyType: Hashable, ValueType> {
values.removeValue(forKey: key)
}
public func contains(_ key: KeyType) -> Bool {
return self.values[key] != nil
}
public func put(_ value: ValueType, forKey key: KeyType) {
self.values[key] = value
}
public func get(_ key: KeyType)->ValueType?{
public func get(_ key: KeyType) -> ValueType? {
return self.values[key]
}

View File

@ -10,12 +10,12 @@ import Foundation
class StreamReader {
let encoding : String.Encoding
let chunkSize : Int
var fileHandle : FileHandle!
let delimData : Data
var buffer : Data
var atEof : Bool
let encoding: String.Encoding
let chunkSize: Int
var fileHandle: FileHandle!
let delimData: Data
var buffer: Data
var atEof: Bool
init?(path: String, delimiter: String = "\n", encoding: String.Encoding = .utf8,
chunkSize: Int = 4096) {

View File

@ -18,20 +18,16 @@ extension String {
return String(self[i] as Character)
}
func unicodeScalar(_ i: Int)->UnicodeScalar{
func unicodeScalar(_ i: Int) -> UnicodeScalar {
return self.unicodeScalars.prefix(i+1).last!
}
func string(_ offset: Int, _ count: Int)->String{
func string(_ offset: Int, _ count: Int) -> String {
let truncStart = self.unicodeScalars.count-offset
return String(self.unicodeScalars.suffix(truncStart).prefix(count))
}
static func split(_ value: String,_ offset : Int, _ count: Int) -> String
{
static func split(_ value: String, _ offset: Int, _ count: Int) -> String {
let start = value.index(value.startIndex, offsetBy: offset)
let end = value.index(value.startIndex, offsetBy: count+offset)
let range = start..<end
@ -46,17 +42,15 @@ extension String {
return (self.trimmingCharacters(in: CharacterSet.whitespaces) == "")
}
func startsWith(_ string:String) -> Bool
{
func startsWith(_ string: String) -> Bool {
return self.hasPrefix(string)
}
func indexOf(_ substring: String, _ offset: Int ) -> Int {
if(offset > characters.count){return -1}
if(offset > characters.count) {return -1}
let maxIndex = self.characters.count - substring.characters.count
if(maxIndex >= 0)
{
if(maxIndex >= 0) {
for index in offset...maxIndex {
let rangeSubstring = self.characters.index(self.startIndex, offsetBy: index)..<self.characters.index(self.startIndex, offsetBy: index + substring.characters.count)
if self.substring(with: rangeSubstring) == substring {
@ -71,31 +65,28 @@ extension String {
return self.indexOf(substring, 0)
}
func trim() -> String {
return trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines)
}
func equalsIgnoreCase(string:String?)->Bool
{
if(string == nil){return false}
func equalsIgnoreCase(string: String?) -> Bool {
if(string == nil) {return false}
return caseInsensitiveCompare(string!) == ComparisonResult.orderedSame
}
static func toHexString(n:Int)->String{
static func toHexString(n: Int) -> String {
return String(format:"%2x", n)
}
func toCharArray() -> [Int] {
return characters.flatMap{Int($0.unicodeScalar.value)}
return characters.flatMap {Int($0.unicodeScalar.value)}
}
func insert(string:String,ind:Int) -> String {
func insert(string: String, ind: Int) -> String {
return String(self.characters.prefix(ind)) + string + String(self.characters.suffix(self.characters.count-ind))
}
func charAt(_ i:Int) -> Character {
func charAt(_ i: Int) -> Character {
return self[i] as Character
}
@ -107,23 +98,22 @@ extension String {
return String.split(self, beginIndex, count)
}
func regionMatches(_ ignoreCase: Bool, _ selfOffset: Int, _ other: String, _ otherOffset: Int, _ length: Int )->Bool{
func regionMatches(_ ignoreCase: Bool, _ selfOffset: Int, _ other: String, _ otherOffset: Int, _ length: Int ) -> Bool {
if ((otherOffset < 0) || (selfOffset < 0)
|| (selfOffset > self.characters.count - length)
|| (otherOffset > other.characters.count - length)) {
return false
}
for i in 0..<length
{
let charSelf : Character = self[i+selfOffset]
let charOther : Character = other[i+otherOffset]
if(ignoreCase){
if(charSelf.lowercase != charOther.lowercase){
for i in 0..<length {
let charSelf: Character = self[i+selfOffset]
let charOther: Character = other[i+otherOffset]
if(ignoreCase) {
if(charSelf.lowercase != charOther.lowercase) {
return false
}
}else{
if(charSelf != charOther){
} else {
if(charSelf != charOther) {
return false
}
}
@ -131,42 +121,40 @@ extension String {
return true
}
func startsWith(_ input: String , _ offset: Int)->Bool
{
func startsWith(_ input: String, _ offset: Int) -> Bool {
if ((offset < 0) || (offset > characters.count - input.characters.count)) {
return false
}
for i in 0..<input.characters.count
{
let charSelf : Character = self[i+offset]
let charOther : Character = input[i]
if(charSelf != charOther){return false}
for i in 0..<input.characters.count {
let charSelf: Character = self[i+offset]
let charOther: Character = input[i]
if(charSelf != charOther) {return false}
}
return true
}
func replaceFirst(of pattern:String,with replacement:String) -> String {
if let range = self.range(of: pattern){
func replaceFirst(of pattern: String, with replacement: String) -> String {
if let range = self.range(of: pattern) {
return self.replacingCharacters(in: range, with: replacement)
}else{
} else {
return self
}
}
func replaceAll(of pattern:String,with replacement:String,options: NCRegularExpression.Options = []) -> String{
do{
func replaceAll(of pattern: String, with replacement: String, options: NCRegularExpression.Options = []) -> String {
do {
let regex = try NCRegularExpression(pattern: pattern, options: [])
let range = NSRange(0..<self.utf16.count)
return regex.stringByReplacingMatches(in: self, options: [],
range: range, withTemplate: replacement)
}catch{
} catch {
NSLog("replaceAll error: \(error)")
return self
}
}
func equals(_ s: String?)->Bool{
if(s == nil){return false}
func equals(_ s: String?) -> Bool {
if(s == nil) {return false}
return self == s!
}
@ -176,15 +164,12 @@ extension String {
}
extension String.Encoding
{
func canEncode(_ string: String) -> Bool
{
extension String.Encoding {
func canEncode(_ string: String) -> Bool {
return string.cString(using: self) != nil
}
public func displayName()->String{
public func displayName() -> String {
switch self {
case String.Encoding.ascii: return "US-ASCII"
case String.Encoding.nextstep: return "nextstep"
@ -214,9 +199,3 @@ extension String.Encoding
}
}
}

View File

@ -46,13 +46,12 @@ open class StringBuilder {
stringValue += string
}
open func appendCodePoint(_ chr: Character) {
stringValue = stringValue + String(chr)
}
open func appendCodePoints(_ chr: [Character]) {
for c in chr{
for c in chr {
stringValue = stringValue + String(c)
}
}
@ -66,12 +65,11 @@ open class StringBuilder {
}
open func appendCodePoints(_ chr: [UnicodeScalar]) {
for c in chr{
for c in chr {
stringValue = stringValue + String(c)
}
}
/**
Append a Printable to the object
@ -86,7 +84,7 @@ open class StringBuilder {
}
@discardableResult
open func insert<T: CustomStringConvertible>(_ offset: Int ,_ value: T) -> StringBuilder {
open func insert<T: CustomStringConvertible>(_ offset: Int, _ value: T) -> StringBuilder {
stringValue = stringValue.insert(string: value.description, ind: offset)
return self
}
@ -160,4 +158,3 @@ public func += <T: CustomStringConvertible>(lhs: StringBuilder, rhs: T) {
public func +(lhs: StringBuilder, rhs: StringBuilder) -> StringBuilder {
return StringBuilder(string: lhs.toString() + rhs.toString())
}

View File

@ -11,8 +11,7 @@ import Foundation
/**
* A minimal String utility class. Designed for internal jsoup use only.
*/
open class StringUtil
{
open class StringUtil {
enum StringError: Error {
case empty
case short
@ -20,7 +19,7 @@ open class StringUtil
}
// memoised padding up to 10
fileprivate static var padding : [String] = ["", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
fileprivate static var padding: [String] = ["", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
/**
* Join a collection of strings by a seperator
@ -28,18 +27,17 @@ open class StringUtil
* @param sep string to place between strings
* @return joined string
*/
open static func join(_ strings:[String], sep:String) -> String {
open static func join(_ strings: [String], sep: String) -> String {
return strings.joined(separator: sep)
}
open static func join(_ strings:Set<String>, sep:String) -> String {
open static func join(_ strings: Set<String>, sep: String) -> String {
return strings.joined(separator: sep)
}
open static func join(_ strings:OrderedSet<String>, sep:String) -> String {
open static func join(_ strings: OrderedSet<String>, sep: String) -> String {
return strings.joined(separator: sep)
}
// /**
// * Join a collection of strings by a seperator
// * @param strings iterator of string objects
@ -66,20 +64,19 @@ open class StringUtil
* @param width amount of padding desired
* @return string of spaces * width
*/
open static func padding(_ width:Int) -> String{
open static func padding(_ width: Int) -> String {
if(width <= 0){
if(width <= 0) {
return ""
}
if (width < padding.count){
if (width < padding.count) {
return padding[width]
}
var out: [Character] = [Character]()
for _ in 0..<width
{
for _ in 0..<width {
out.append(" ")
}
return String(out)
@ -90,14 +87,13 @@ open class StringUtil
* @param string string to test
* @return if string is blank
*/
open static func isBlank(_ string:String) -> Bool {
if (string.characters.count == 0){
open static func isBlank(_ string: String) -> Bool {
if (string.characters.count == 0) {
return true
}
for chr in string.characters
{
if (!StringUtil.isWhitespace(chr)){
for chr in string.characters {
if (!StringUtil.isWhitespace(chr)) {
return false
}
}
@ -109,15 +105,13 @@ open class StringUtil
* @param string string to test
* @return true if only digit chars, false if empty or null or contains non-digit chrs
*/
open static func isNumeric(_ string:String) -> Bool {
if (string.characters.count == 0){
open static func isNumeric(_ string: String) -> Bool {
if (string.characters.count == 0) {
return false
}
for chr in string.characters
{
if !("0"..."9" ~= chr)
{
for chr in string.characters {
if !("0"..."9" ~= chr) {
return false
}
}
@ -129,8 +123,7 @@ open class StringUtil
* @param c code point to test
* @return true if code point is whitespace, false otherwise
*/
open static func isWhitespace(_ c:Character) -> Bool
{
open static func isWhitespace(_ c: Character) -> Bool {
//(c == " " || c == "\t" || c == "\n" || (c == "\f" ) || c == "\r")
return c.isWhitespace
}
@ -141,8 +134,8 @@ open class StringUtil
* @param string content to normalise
* @return normalised string
*/
open static func normaliseWhitespace(_ string:String) -> String {
let sb : StringBuilder = StringBuilder.init()
open static func normaliseWhitespace(_ string: String) -> String {
let sb: StringBuilder = StringBuilder.init()
appendNormalisedWhitespace(sb, string: string, stripLeading: false)
return sb.toString()
}
@ -153,22 +146,18 @@ open class StringUtil
* @param string string to normalize whitespace within
* @param stripLeading set to true if you wish to remove any leading whitespace
*/
open static func appendNormalisedWhitespace(_ accum:StringBuilder, string: String , stripLeading:Bool ) {
var lastWasWhite : Bool = false
open static func appendNormalisedWhitespace(_ accum: StringBuilder, string: String, stripLeading: Bool ) {
var lastWasWhite: Bool = false
var reachedNonWhite: Bool = false
for c in string.characters
{
if (isWhitespace(c))
{
if ((stripLeading && !reachedNonWhite) || lastWasWhite){
for c in string.characters {
if (isWhitespace(c)) {
if ((stripLeading && !reachedNonWhite) || lastWasWhite) {
continue
}
accum.append(" ")
lastWasWhite = true
}
else
{
} else {
accum.appendCodePoint(c)
lastWasWhite = false
reachedNonWhite = true
@ -176,25 +165,24 @@ open class StringUtil
}
}
open static func inString(_ needle:String? , haystack:String...) -> Bool {
return inString(needle,haystack)
open static func inString(_ needle: String?, haystack: String...) -> Bool {
return inString(needle, haystack)
}
open static func inString(_ needle:String? , _ haystack:[String?]) -> Bool {
if(needle == nil){return false}
for hay in haystack
{
if(hay != nil && hay!.compare(needle!) == ComparisonResult.orderedSame){
open static func inString(_ needle: String?, _ haystack: [String?]) -> Bool {
if(needle == nil) {return false}
for hay in haystack {
if(hay != nil && hay!.compare(needle!) == ComparisonResult.orderedSame) {
return true
}
}
return false
}
open static func inSorted(_ needle:String, haystack:[String]) -> Bool {
open static func inSorted(_ needle: String, haystack: [String]) -> Bool {
return binarySearch(haystack, searchItem: needle) >= 0
}
open static func binarySearch<T:Comparable>(_ inputArr:Array<T>, searchItem: T)->Int{
open static func binarySearch<T: Comparable>(_ inputArr: Array<T>, searchItem: T) -> Int {
var lowerIndex = 0
var upperIndex = inputArr.count - 1
@ -222,10 +210,9 @@ open class StringUtil
* @throws MalformedURLException if an error occurred generating the URL
*/
//NOTE: Not sure it work
open static func resolve(_ base:URL, relUrl: String ) -> URL? {
open static func resolve(_ base: URL, relUrl: String ) -> URL? {
var base = base
if(base.pathComponents.count == 0 && base.absoluteString.characters.last != "/" && !base.isFileURL)
{
if(base.pathComponents.count == 0 && base.absoluteString.characters.last != "/" && !base.isFileURL) {
base = base.appendingPathComponent("/", isDirectory: false)
}
let u = URL(string: relUrl, relativeTo : base)
@ -238,22 +225,21 @@ open class StringUtil
* @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
*/
open static func resolve(_ baseUrl : String , relUrl : String ) -> String {
open static func resolve(_ baseUrl: String, relUrl: String ) -> String {
let base = URL(string: baseUrl)
if(base == nil || base?.scheme == nil)
{
if(base == nil || base?.scheme == nil) {
let abs = URL(string: relUrl)
return abs != nil && abs?.scheme != nil ? abs!.absoluteURL.absoluteString : ""
}else{
} else {
let url = resolve(base!, relUrl: relUrl)
if(url != nil){
if(url != nil) {
let ext = url!.absoluteURL.absoluteString
return ext
}
if(base != nil && base?.scheme != nil){
if(base != nil && base?.scheme != nil) {
let ext = base!.absoluteString
return ext
}
@ -277,15 +263,3 @@ open class StringUtil
}
}

View File

@ -11,83 +11,81 @@ import Foundation
/**
* Base structural evaluator.
*/
public class StructuralEvaluator : Evaluator {
public class StructuralEvaluator: Evaluator {
let evaluator: Evaluator
public init(_ evaluator: Evaluator) {
self.evaluator = evaluator
}
public class Root : Evaluator {
public override func matches(_ root: Element, _ element: Element)->Bool {
public class Root: Evaluator {
public override func matches(_ root: Element, _ element: Element) -> Bool {
return root === element
}
}
public class Has : StructuralEvaluator {
public class Has: StructuralEvaluator {
public override init(_ evaluator: Evaluator) {
super.init(evaluator)
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
for e in try element.getAllElements().array() {
do{
if(e != element){
if ((try evaluator.matches(root, e)))
{
do {
if(e != element) {
if ((try evaluator.matches(root, e))) {
return true
}
}
}catch{}
} catch {}
}
return false
}
public override func toString()->String {
public override func toString() -> String {
return ":has(\(evaluator.toString()))"
}
}
public class Not : StructuralEvaluator {
public class Not: StructuralEvaluator {
public override init(_ evaluator: Evaluator) {
super.init(evaluator)
}
public override func matches(_ root: Element, _ node: Element)->Bool {
do{
public override func matches(_ root: Element, _ node: Element) -> Bool {
do {
return try !evaluator.matches(root, node)
}catch{}
} catch {}
return false
}
public override func toString()->String {
public override func toString() -> String {
return ":not\(evaluator.toString())"
}
}
public class Parent : StructuralEvaluator {
public class Parent: StructuralEvaluator {
public override init(_ evaluator: Evaluator) {
super.init(evaluator)
}
public override func matches(_ root: Element, _ element: Element)->Bool {
if (root == element){
public override func matches(_ root: Element, _ element: Element) -> Bool {
if (root == element) {
return false
}
var parent = element.parent()
while (true) {
do{
if parent != nil{
if (try evaluator.matches(root, parent!)){
do {
if parent != nil {
if (try evaluator.matches(root, parent!)) {
return true
}
}
}catch{}
} catch {}
if (parent == root){
if (parent == root) {
break
}
parent = parent?.parent()
@ -95,83 +93,83 @@ public class StructuralEvaluator : Evaluator {
return false
}
public override func toString()->String {
public override func toString() -> String {
return ":parent\(evaluator.toString())"
}
}
public class ImmediateParent : StructuralEvaluator {
public class ImmediateParent: StructuralEvaluator {
public override init(_ evaluator: Evaluator) {
super.init(evaluator)
}
public override func matches(_ root: Element, _ element: Element)->Bool {
if (root == element){
public override func matches(_ root: Element, _ element: Element) -> Bool {
if (root == element) {
return false
}
if let parent = element.parent(){
do{
if let parent = element.parent() {
do {
return try evaluator.matches(root, parent)
}catch{}
} catch {}
}
return false
}
public override func toString()->String {
public override func toString() -> String {
return ":ImmediateParent\(evaluator.toString())"
}
}
public class PreviousSibling : StructuralEvaluator {
public class PreviousSibling: StructuralEvaluator {
public override init(_ evaluator: Evaluator) {
super.init(evaluator)
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
if (root == element){
if (root == element) {
return false
}
var prev = try element.previousElementSibling()
while (prev != nil) {
do{
if (try evaluator.matches(root, prev!)){
do {
if (try evaluator.matches(root, prev!)) {
return true
}
}catch{}
} catch {}
prev = try prev!.previousElementSibling()
}
return false
}
public override func toString()->String {
public override func toString() -> String {
return ":prev*\(evaluator.toString())"
}
}
class ImmediatePreviousSibling : StructuralEvaluator {
class ImmediatePreviousSibling: StructuralEvaluator {
public override init(_ evaluator: Evaluator) {
super.init(evaluator)
}
public override func matches(_ root: Element, _ element: Element)throws->Bool {
if (root == element){
if (root == element) {
return false
}
if let prev = try element.previousElementSibling(){
do{
if let prev = try element.previousElementSibling() {
do {
return try evaluator.matches(root, prev)
}catch{}
} catch {}
}
return false
}
public override func toString()->String {
public override func toString() -> String {
return ":prev\(evaluator.toString())"
}
}

View File

@ -8,12 +8,11 @@
import Foundation
/**
The core public access point to the jsoup functionality.
*/
open class SwiftSoup {
private init(){}
private init() {}
/**
Parse HTML into a Document. The parser will make a sensible, balanced document tree out of any HTML.
@ -234,5 +233,4 @@ open class SwiftSoup {
return try clean.body()?.html()
}
}

View File

@ -8,28 +8,27 @@
import Foundation
open class Tag : Hashable
{
open class Tag: Hashable {
// map of known tags
static var tags: Dictionary<String, Tag> = {
do{
do {
return try Tag.initializeMaps()
}catch{
} catch {
preconditionFailure("This method must be overridden")
}
return Dictionary<String, Tag>()
}()
fileprivate var _tagName : String
fileprivate var _isBlock : Bool = true // block or inline
fileprivate var _formatAsBlock : Bool = true // should be formatted as a block
fileprivate var _canContainBlock : Bool = true // Can this tag hold block level tags?
fileprivate var _canContainInline : Bool = true // only pcdata if not
fileprivate var _empty : Bool = false // can hold nothing e.g. img
fileprivate var _selfClosing : Bool = false // can self close (<foo />). used for unknown tags that self close, without forcing them as empty.
fileprivate var _preserveWhitespace : Bool = false // for pre, textarea, script etc
fileprivate var _formList : Bool = false // a control that appears in forms: input, textarea, output etc
fileprivate var _formSubmit : Bool = false // a control that can be submitted in a form: input etc
fileprivate var _tagName: String
fileprivate var _isBlock: Bool = true // block or inline
fileprivate var _formatAsBlock: Bool = true // should be formatted as a block
fileprivate var _canContainBlock: Bool = true // Can this tag hold block level tags?
fileprivate var _canContainInline: Bool = true // only pcdata if not
fileprivate var _empty: Bool = false // can hold nothing e.g. img
fileprivate var _selfClosing: Bool = false // can self close (<foo />). used for unknown tags that self close, without forcing them as empty.
fileprivate var _preserveWhitespace: Bool = false // for pre, textarea, script etc
fileprivate var _formList: Bool = false // a control that appears in forms: input, textarea, output etc
fileprivate var _formSubmit: Bool = false // a control that can be submitted in a form: input etc
public init(_ tagName: String) {
self._tagName = tagName
@ -40,7 +39,7 @@ open class Tag : Hashable
*
* @return the tag's name
*/
open func getName()->String {
open func getName() -> String {
return self._tagName
}
@ -56,7 +55,7 @@ open class Tag : Hashable
*/
open static func valueOf(_ tagName: String, _ settings: ParseSettings)throws->Tag {
var tagName = tagName
var tag : Tag? = Tag.tags[tagName]
var tag: Tag? = Tag.tags[tagName]
if (tag == nil) {
tagName = settings.normalizeTag(tagName)
@ -91,7 +90,7 @@ open class Tag : Hashable
*
* @return if block tag
*/
open func isBlock()->Bool {
open func isBlock() -> Bool {
return _isBlock
}
@ -100,7 +99,7 @@ open class Tag : Hashable
*
* @return if should be formatted as block or inline
*/
open func formatAsBlock()->Bool {
open func formatAsBlock() -> Bool {
return _formatAsBlock
}
@ -109,7 +108,7 @@ open class Tag : Hashable
*
* @return if tag can contain block tags
*/
open func canContainBlock()->Bool {
open func canContainBlock() -> Bool {
return _canContainBlock
}
@ -118,7 +117,7 @@ open class Tag : Hashable
*
* @return if this tag is an inline tag.
*/
open func isInline()->Bool {
open func isInline() -> Bool {
return !_isBlock
}
@ -127,7 +126,7 @@ open class Tag : Hashable
*
* @return if this tag is a data only tag
*/
open func isData()->Bool {
open func isData() -> Bool {
return !_canContainInline && !isEmpty()
}
@ -136,7 +135,7 @@ open class Tag : Hashable
*
* @return if this is an empty tag
*/
open func isEmpty()->Bool {
open func isEmpty() -> Bool {
return _empty
}
@ -145,7 +144,7 @@ open class Tag : Hashable
*
* @return if this tag should be output as self closing.
*/
open func isSelfClosing()->Bool {
open func isSelfClosing() -> Bool {
return _empty || _selfClosing
}
@ -154,7 +153,7 @@ open class Tag : Hashable
*
* @return if a known tag
*/
open func isKnownTag()->Bool {
open func isKnownTag() -> Bool {
return Tag.tags[_tagName] != nil
}
@ -164,7 +163,7 @@ open class Tag : Hashable
* @param tagName name of tag
* @return if known HTML tag
*/
open static func isKnownTag(_ tagName: String)->Bool {
open static func isKnownTag(_ tagName: String) -> Bool {
return Tag.tags[tagName] != nil
}
@ -173,7 +172,7 @@ open class Tag : Hashable
*
* @return if preserve whitepace
*/
public func preserveWhitespace()->Bool {
public func preserveWhitespace() -> Bool {
return _preserveWhitespace
}
@ -181,7 +180,7 @@ open class Tag : Hashable
* Get if this tag represents a control associated with a form. E.g. input, textarea, output
* @return if associated with a form
*/
public func isFormListed()->Bool {
public func isFormListed() -> Bool {
return _formList
}
@ -189,12 +188,12 @@ open class Tag : Hashable
* Get if this tag represents an element that should be submitted with a form. E.g. input, option
* @return if submittable with a form
*/
public func isFormSubmittable()->Bool {
public func isFormSubmittable() -> Bool {
return _formSubmit
}
@discardableResult
func setSelfClosing() ->Tag{
func setSelfClosing() -> Tag {
_selfClosing = true
return self
}
@ -207,14 +206,13 @@ open class Tag : Hashable
/// - Parameters:
/// - lhs: A value to compare.
/// - rhs: Another value to compare.
static public func ==(lhs: Tag, rhs: Tag) -> Bool
{
static public func ==(lhs: Tag, rhs: Tag) -> Bool {
let this = lhs
let o = rhs
if (this === o) {return true}
if (type(of:this) != type(of:o)) {return false}
let tag : Tag = o
let tag: Tag = o
if (lhs._tagName != tag._tagName) {return false}
if (lhs._canContainBlock != tag._canContainBlock) {return false}
@ -228,8 +226,7 @@ open class Tag : Hashable
return lhs._formSubmit == tag._formSubmit
}
public func equals(_ tag : Tag)->Bool
{
public func equals(_ tag: Tag) -> Bool {
return self == tag
}
@ -237,9 +234,8 @@ open class Tag : Hashable
///
/// Hash values are not guaranteed to be equal across different executions of
/// your program. Do not save hash values to use during a future execution.
public var hashValue: Int
{
var result : Int = _tagName.hashValue
public var hashValue: Int {
var result: Int = _tagName.hashValue
result = Int.addWithOverflow(Int.multiplyWithOverflow(31, result).0, _isBlock ? 1 : 0).0
result = Int.addWithOverflow(Int.multiplyWithOverflow(31, result).0, _formatAsBlock ? 1 : 0).0
result = Int.addWithOverflow(Int.multiplyWithOverflow(31, result).0, _canContainBlock ? 1 : 0).0
@ -252,14 +248,13 @@ open class Tag : Hashable
return result
}
open func toString()->String {
open func toString() -> String {
return _tagName
}
// internal static initialisers:
// prepped from http://www.w3.org/TR/REC-html40/sgml/dtd.html and other sources
private static let blockTags : [String] = [
private static let blockTags: [String] = [
"html", "head", "body", "frameset", "script", "noscript", "style", "meta", "link", "title", "frame",
"noframes", "section", "nav", "aside", "hgroup", "header", "footer", "p", "h1", "h2", "h3", "h4", "h5", "h6",
"ul", "ol", "pre", "div", "blockquote", "hr", "address", "figure", "figcaption", "form", "fieldset", "ins",
@ -267,7 +262,7 @@ open class Tag : Hashable
"td", "video", "audio", "canvas", "details", "menu", "plaintext", "template", "article", "main",
"svg", "math"
]
private static let inlineTags : [String] = [
private static let inlineTags: [String] = [
"object", "base", "font", "tt", "i", "b", "u", "big", "small", "em", "strong", "dfn", "code", "samp", "kbd",
"var", "cite", "abbr", "time", "acronym", "mark", "ruby", "rt", "rp", "a", "img", "br", "wbr", "map", "q",
"sub", "sup", "bdo", "iframe", "embed", "span", "input", "select", "textarea", "label", "button", "optgroup",
@ -275,29 +270,27 @@ open class Tag : Hashable
"summary", "command", "device", "area", "basefont", "bgsound", "menuitem", "param", "source", "track",
"data", "bdi"
]
private static let emptyTags : [String] = [
private static let emptyTags: [String] = [
"meta", "link", "base", "frame", "img", "br", "wbr", "embed", "hr", "input", "keygen", "col", "command",
"device", "area", "basefont", "bgsound", "menuitem", "param", "source", "track"
]
private static let formatAsInlineTags : [String] = [
private static let formatAsInlineTags: [String] = [
"title", "a", "p", "h1", "h2", "h3", "h4", "h5", "h6", "pre", "address", "li", "th", "td", "script", "style",
"ins", "del", "s"
]
private static let preserveWhitespaceTags : [String] = [
private static let preserveWhitespaceTags: [String] = [
"pre", "plaintext", "title", "textarea"
// script is not here as it is a data node, which always preserve whitespace
]
// todo: I think we just need submit tags, and can scrub listed
private static let formListedTags : [String] = [
private static let formListedTags: [String] = [
"button", "fieldset", "input", "keygen", "object", "output", "select", "textarea"
]
private static let formSubmitTags : [String] = [
private static let formSubmitTags: [String] = [
"input", "keygen", "object", "select", "textarea"
]
static private func initializeMaps()throws->Dictionary<String, Tag>
{
static private func initializeMaps()throws->Dictionary<String, Tag> {
var dict = Dictionary<String, Tag>()
// creates
@ -348,9 +341,3 @@ open class Tag : Hashable
return dict
}
}

View File

@ -11,14 +11,14 @@ import Foundation
/**
A text node.
*/
open class TextNode : Node {
open class TextNode: Node {
/*
TextNode is a node, and so by default comes with attributes and children. The attributes are seldom used, but use
memory, and the child nodes are never used. So we don't have them, and override accessors to attributes to create
them as needed on the fly.
*/
private static let TEXT_KEY : String = "text"
var _text : String
private static let TEXT_KEY: String = "text"
var _text: String
/**
Create a new TextNode representing the supplied (unencoded) text).
@ -34,7 +34,7 @@ open class TextNode : Node {
}
open override func nodeName()->String {
open override func nodeName() -> String {
return "#text"
}
@ -43,7 +43,7 @@ open class TextNode : Node {
* @return Unencoded, normalised text.
* @see TextNode#getWholeText()
*/
open func text()->String {
open func text() -> String {
return TextNode.normaliseWhitespace(getWholeText())
}
@ -53,14 +53,14 @@ open class TextNode : Node {
* @return this, for chaining
*/
@discardableResult
public func text(_ text: String)->TextNode {
public func text(_ text: String) -> TextNode {
self._text = text
guard let attributes = attributes else {
return self
}
do{
do {
try attributes.put(TextNode.TEXT_KEY, text)
}catch{
} catch {
}
return self
@ -70,7 +70,7 @@ open class TextNode : Node {
Get the (unencoded) text of this text node, including any newlines and spaces present in the original.
@return text
*/
open func getWholeText()->String {
open func getWholeText() -> String {
return attributes == nil ? _text : attributes!.get(key: TextNode.TEXT_KEY)
}
@ -78,7 +78,7 @@ open class TextNode : Node {
Test if this text node is blank -- that is, empty or only whitespace (including newlines).
@return true if this document is empty or only whitespace, false if it contains any text content.
*/
open func isBlank()->Bool {
open func isBlank() -> Bool {
return StringUtil.isBlank(getWholeText())
}
@ -92,25 +92,24 @@ open class TextNode : Node {
try Validate.isTrue(val: offset >= 0, msg: "Split offset must be not be negative")
try Validate.isTrue(val: offset < _text.characters.count, msg: "Split offset must not be greater than current text length")
let head : String = getWholeText().substring(0, offset)
let tail : String = getWholeText().substring(offset)
let head: String = getWholeText().substring(0, offset)
let tail: String = getWholeText().substring(offset)
text(head)
let tailNode : TextNode = TextNode(tail, self.getBaseUri())
if (parent() != nil){
let tailNode: TextNode = TextNode(tail, self.getBaseUri())
if (parent() != nil) {
try parent()?.addChildren(siblingIndex+1, tailNode)
}
return tailNode
}
override func outerHtmlHead(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings)throws
{
override func outerHtmlHead(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings)throws {
if (out.prettyPrint() &&
((siblingIndex == 0 && (parentNode as? Element) != nil && (parentNode as! Element).tag().formatAsBlock() && !isBlank()) ||
(out.outline() && siblingNodes().count > 0 && !isBlank()) )){
(out.outline() && siblingNodes().count > 0 && !isBlank()) )) {
indent(accum, depth, out)
}
let par : Element? = parent() as? Element
let par: Element? = parent() as? Element
let normaliseWhite = out.prettyPrint() && par != nil && !Element.preserveWhitespace(par!)
Entities.escape(accum, getWholeText(), out, false, normaliseWhite, false)
@ -119,7 +118,6 @@ open class TextNode : Node {
override func outerHtmlTail(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {
}
open override func toString()throws->String {
return try outerHtml()
}
@ -131,21 +129,21 @@ open class TextNode : Node {
* @return TextNode containing unencoded data (e.g. &lt;)
*/
open static func createFromEncoded(_ encodedText: String, _ baseUri: String)throws->TextNode {
let text : String = try Entities.unescape(encodedText)
let text: String = try Entities.unescape(encodedText)
return TextNode(text, baseUri)
}
static open func normaliseWhitespace(_ text: String)->String {
static open func normaliseWhitespace(_ text: String) -> String {
let _text = StringUtil.normaliseWhitespace(text)
return _text
}
static open func stripLeadingWhitespace(_ text: String)->String {
static open func stripLeadingWhitespace(_ text: String) -> String {
return text.replaceFirst(of: "^\\s+", with: "")
//return text.replaceFirst("^\\s+", "")
}
static open func lastCharIsWhitespace(_ sb: StringBuilder)->Bool {
static open func lastCharIsWhitespace(_ sb: StringBuilder) -> Bool {
return sb.toString().characters.last == " "
}
@ -153,19 +151,18 @@ open class TextNode : Node {
private func ensureAttributes() {
if (attributes == nil) {
attributes = Attributes()
do{
do {
try attributes?.put(TextNode.TEXT_KEY, _text)
}catch{}
} catch {}
}
}
open override func attr(_ attributeKey: String)throws->String {
ensureAttributes()
return try super.attr(attributeKey)
}
open override func getAttributes()->Attributes {
open override func getAttributes() -> Attributes {
ensureAttributes()
return super.getAttributes()!
}
@ -175,38 +172,32 @@ open class TextNode : Node {
return try super.attr(attributeKey, attributeValue)
}
open override func hasAttr(_ attributeKey: String)->Bool {
open override func hasAttr(_ attributeKey: String) -> Bool {
ensureAttributes()
return super.hasAttr(attributeKey)
}
open override func removeAttr(_ attributeKey: String)throws->Node {
ensureAttributes()
return try super.removeAttr(attributeKey)
}
open override func absUrl(_ attributeKey: String)throws->String {
ensureAttributes()
return try super.absUrl(attributeKey)
}
public override func copy(with zone: NSZone? = nil) -> Any
{
let clone = TextNode(_text,baseUri)
public override func copy(with zone: NSZone? = nil) -> Any {
let clone = TextNode(_text, baseUri)
return super.copy(clone: clone)
}
public override func copy(parent: Node?)->Node
{
let clone = TextNode(_text,baseUri)
return super.copy(clone: clone,parent: parent)
public override func copy(parent: Node?) -> Node {
let clone = TextNode(_text, baseUri)
return super.copy(clone: clone, parent: parent)
}
public override func copy(clone: Node, parent: Node?)->Node
{
return super.copy(clone: clone,parent: parent)
public override func copy(clone: Node, parent: Node?) -> Node {
return super.copy(clone: clone, parent: parent)
}
}

View File

@ -8,15 +8,13 @@
import Foundation
open class Token
{
var type : TokenType = TokenType.Doctype
open class Token {
var type: TokenType = TokenType.Doctype
private init() {
}
func tokenType()->String
{
func tokenType() -> String {
return String(describing: type(of: self))
}
@ -25,8 +23,7 @@ open class Token
* piece of data, which immediately get GCed.
*/
@discardableResult
public func reset()->Token
{
public func reset() -> Token {
preconditionFailure("This method must be overridden")
}
@ -38,11 +35,11 @@ open class Token
return String(describing: type(of: self))
}
final class Doctype : Token {
final class Doctype: Token {
let name: StringBuilder = StringBuilder()
let publicIdentifier: StringBuilder = StringBuilder()
let systemIdentifier : StringBuilder = StringBuilder()
var forceQuirks : Bool = false
let systemIdentifier: StringBuilder = StringBuilder()
var forceQuirks: Bool = false
override init() {
super.init()
@ -50,7 +47,7 @@ open class Token
}
@discardableResult
override func reset()->Token {
override func reset() -> Token {
Token.reset(name)
Token.reset(publicIdentifier)
Token.reset(systemIdentifier)
@ -58,41 +55,41 @@ open class Token
return self
}
func getName()->String {
func getName() -> String {
return name.toString()
}
func getPublicIdentifier()->String {
func getPublicIdentifier() -> String {
return publicIdentifier.toString()
}
open func getSystemIdentifier()->String {
open func getSystemIdentifier() -> String {
return systemIdentifier.toString()
}
open func isForceQuirks()->Bool {
open func isForceQuirks() -> Bool {
return forceQuirks
}
}
class Tag : Token {
class Tag: Token {
public var _tagName: String?
public var _normalName: String? // lc version of tag name, for case insensitive tree build
private var _pendingAttributeName: String? // attribute names are generally caught in one hop, not accumulated
private let _pendingAttributeValue : StringBuilder = StringBuilder() // but values are accumulated, from e.g. & in hrefs
private let _pendingAttributeValue: StringBuilder = StringBuilder() // but values are accumulated, from e.g. & in hrefs
private var _pendingAttributeValueS: String? // try to get attr vals in one shot, vs Builder
private var _hasEmptyAttributeValue : Bool = false // distinguish boolean attribute from empty string value
private var _hasEmptyAttributeValue: Bool = false // distinguish boolean attribute from empty string value
private var _hasPendingAttributeValue: Bool = false
public var _selfClosing : Bool = false
public var _selfClosing: Bool = false
// start tags get attributes on construction. End tags get attributes on first new attribute (but only for parser convenience, not used).
public var _attributes : Attributes = Attributes()
public var _attributes: Attributes = Attributes()
override init() {
super.init()
}
@discardableResult
override func reset()->Tag {
override func reset() -> Tag {
_tagName = nil
_normalName = nil
_pendingAttributeName = nil
@ -111,12 +108,12 @@ open class Token
// }
if (_pendingAttributeName != nil) {
var attribute : Attribute
if (_hasPendingAttributeValue){
attribute = try Attribute(key: _pendingAttributeName!,value: _pendingAttributeValue.length > 0 ? _pendingAttributeValue.toString() : _pendingAttributeValueS!)
}else if (_hasEmptyAttributeValue){
var attribute: Attribute
if (_hasPendingAttributeValue) {
attribute = try Attribute(key: _pendingAttributeName!, value: _pendingAttributeValue.length > 0 ? _pendingAttributeValue.toString() : _pendingAttributeValueS!)
} else if (_hasEmptyAttributeValue) {
attribute = try Attribute(key: _pendingAttributeName!, value: "")
}else{
} else {
attribute = try BooleanAttribute(key: _pendingAttributeName!)
}
_attributes.put(attribute: attribute)
@ -141,22 +138,22 @@ open class Token
return _tagName!
}
func normalName()->String? { // loses case, used in tree building for working out where in tree it should go
func normalName() -> String? { // loses case, used in tree building for working out where in tree it should go
return _normalName
}
@discardableResult
func name(_ name: String)->Tag {
func name(_ name: String) -> Tag {
_tagName = name
_normalName = name.lowercased()
return self
}
func isSelfClosing()->Bool {
func isSelfClosing() -> Bool {
return _selfClosing
}
func getAttributes()->Attributes {
func getAttributes() -> Attributes {
return _attributes
}
@ -166,7 +163,7 @@ open class Token
_normalName = _tagName?.lowercased()
}
func appendTagName(_ append : UnicodeScalar) {
func appendTagName(_ append: UnicodeScalar) {
appendTagName("\(append)")
}
@ -218,7 +215,7 @@ open class Token
}
}
final class StartTag : Tag {
final class StartTag: Tag {
override init() {
super.init()
_attributes = Attributes()
@ -226,7 +223,7 @@ open class Token
}
@discardableResult
override func reset()->Tag {
override func reset() -> Tag {
super.reset()
_attributes = Attributes()
// todo - would prefer these to be null, but need to check Element assertions
@ -234,7 +231,7 @@ open class Token
}
@discardableResult
func nameAttr(_ name: String, _ attributes: Attributes)->StartTag {
func nameAttr(_ name: String, _ attributes: Attributes) -> StartTag {
self._tagName = name
self._attributes = attributes
_normalName = _tagName?.lowercased()
@ -242,32 +239,31 @@ open class Token
}
open override func toString()throws->String {
if (_attributes.size() > 0){
if (_attributes.size() > 0) {
return try "<" + (name()) + " " + (_attributes.toString()) + ">"
}else{
} else {
return try "<" + name() + ">"
}
}
}
final class EndTag : Tag{
final class EndTag: Tag {
override init() {
super.init()
type = TokenType.EndTag
}
open override func toString()throws->String {
return "</" + (try name()) + ">"
}
}
final class Comment : Token {
let data : StringBuilder = StringBuilder()
var bogus : Bool = false
final class Comment: Token {
let data: StringBuilder = StringBuilder()
var bogus: Bool = false
@discardableResult
override func reset()->Token {
override func reset() -> Token {
Token.reset(data)
bogus = false
return self
@ -278,18 +274,17 @@ open class Token
type = TokenType.Comment
}
func getData()->String {
func getData() -> String {
return data.toString()
}
open override func toString()throws->String {
return "<!--" + getData() + "-->"
}
}
final class Char : Token {
public var data : String?
final class Char: Token {
public var data: String?
override init() {
super.init()
@ -297,85 +292,83 @@ open class Token
}
@discardableResult
override func reset()->Token {
override func reset() -> Token {
data = nil
return self
}
@discardableResult
func data(_ data: String)->Char {
func data(_ data: String) -> Char {
self.data = data
return self
}
func getData()->String? {
func getData() -> String? {
return data
}
open override func toString()throws->String {
try Validate.notNull(obj: data)
return getData()!
}
}
final class EOF : Token {
final class EOF: Token {
override init() {
super.init()
type = Token.TokenType.EOF
}
@discardableResult
override func reset()->Token {
override func reset() -> Token {
return self
}
}
func isDoctype()->Bool {
func isDoctype() -> Bool {
return type == TokenType.Doctype
}
func asDoctype()->Doctype {
func asDoctype() -> Doctype {
return self as! Doctype
}
func isStartTag()->Bool {
func isStartTag() -> Bool {
return type == TokenType.StartTag
}
func asStartTag()->StartTag {
func asStartTag() -> StartTag {
return self as! StartTag
}
func isEndTag()->Bool {
func isEndTag() -> Bool {
return type == TokenType.EndTag
}
func asEndTag()->EndTag {
func asEndTag() -> EndTag {
return self as! EndTag
}
func isComment()->Bool {
func isComment() -> Bool {
return type == TokenType.Comment
}
func asComment()->Comment {
func asComment() -> Comment {
return self as! Comment
}
func isCharacter()->Bool {
func isCharacter() -> Bool {
return type == TokenType.Char
}
func asCharacter()->Char {
func asCharacter() -> Char {
return self as! Char
}
func isEOF()->Bool {
func isEOF() -> Bool {
return type == TokenType.EOF
}
public enum TokenType {
case Doctype
case StartTag

View File

@ -8,12 +8,11 @@
import Foundation
open class TokenQueue
{
private var queue : String
private var pos : Int = 0
open class TokenQueue {
private var queue: String
private var pos: Int = 0
private static let ESC : Character = "\\" // escape char for chomp balanced.
private static let ESC: Character = "\\" // escape char for chomp balanced.
/**
Create a new TokenQueue.
@ -27,11 +26,11 @@ open class TokenQueue
* Is the queue empty?
* @return true if no data left in queue.
*/
open func isEmpty()->Bool {
open func isEmpty() -> Bool {
return remainingLength() == 0
}
private func remainingLength()->Int {
private func remainingLength() -> Int {
return queue.characters.count - pos
}
@ -39,7 +38,7 @@ open class TokenQueue
* Retrieves but does not remove the first character from the queue.
* @return First character, or 0 if empty.
*/
open func peek()-> Character {
open func peek() -> Character {
return isEmpty() ? Character(UnicodeScalar(0)) : queue[pos]
}
@ -66,7 +65,7 @@ open class TokenQueue
* @param seq String to check queue for.
* @return true if the next characters match.
*/
open func matches(_ seq: String)->Bool {
open func matches(_ seq: String) -> Bool {
return queue.regionMatches(true, pos, seq, 0, seq.characters.count)
}
@ -75,7 +74,7 @@ open class TokenQueue
* @param seq string to case sensitively check for
* @return true if matched, false if not
*/
open func matchesCS(_ seq: String)->Bool {
open func matchesCS(_ seq: String) -> Bool {
return queue.startsWith(seq, pos)
}
@ -84,32 +83,32 @@ open class TokenQueue
@param seq list of strings to case insensitively check for
@return true of any matched, false if none did
*/
open func matchesAny(_ seq:[String])->Bool {
open func matchesAny(_ seq: [String]) -> Bool {
for s in seq {
if (matches(s)){
if (matches(s)) {
return true
}
}
return false
}
open func matchesAny(_ seq: String...)->Bool {
open func matchesAny(_ seq: String...) -> Bool {
return matchesAny(seq)
}
open func matchesAny(_ seq: Character...)->Bool {
if (isEmpty()){
open func matchesAny(_ seq: Character...) -> Bool {
if (isEmpty()) {
return false
}
for c in seq {
if (queue[pos] as Character == c){
if (queue[pos] as Character == c) {
return true
}
}
return false
}
open func matchesStartTag()->Bool {
open func matchesStartTag() -> Bool {
// micro opt for matching "<x"
return (remainingLength() >= 2 && queue[pos] as Character == "<" && Character.isLetter(queue.charAt(pos+1)))
}
@ -121,7 +120,7 @@ open class TokenQueue
* @return true if found and removed, false if not found.
*/
@discardableResult
open func matchChomp(_ seq: String)->Bool {
open func matchChomp(_ seq: String) -> Bool {
if (matches(seq)) {
pos += seq.characters.count
return true
@ -134,7 +133,7 @@ open class TokenQueue
Tests if queue starts with a whitespace character.
@return if starts with whitespace
*/
open func matchesWhitespace()->Bool {
open func matchesWhitespace() -> Bool {
return !isEmpty() && StringUtil.isWhitespace(queue.charAt(pos))
}
@ -142,7 +141,7 @@ open class TokenQueue
Test if the queue matches a word character (letter or digit).
@return if matches a word character
*/
open func matchesWord()->Bool {
open func matchesWord() -> Bool {
return !isEmpty() && (Character.isLetterOrDigit(queue.charAt(pos)))
}
@ -158,7 +157,7 @@ open class TokenQueue
* Consume one character off queue.
* @return first character on queue.
*/
open func consume()->Character {
open func consume() -> Character {
let i = pos
pos+=1
return queue.charAt(i)
@ -172,12 +171,12 @@ open class TokenQueue
* @param seq sequence to remove from head of queue.
*/
open func consume(_ seq: String)throws {
if (!matches(seq)){
if (!matches(seq)) {
//throw new IllegalStateException("Queue did not match expected sequence")
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Queue did not match expected sequence")
}
let len = seq.characters.count
if (len > remainingLength()){
if (len > remainingLength()) {
//throw new IllegalStateException("Queue not long enough to consume sequence")
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Queue not long enough to consume sequence")
}
@ -191,7 +190,7 @@ open class TokenQueue
* @return The matched data consumed from queue.
*/
@discardableResult
open func consumeTo(_ seq: String)->String {
open func consumeTo(_ seq: String) -> String {
let offset = queue.indexOf(seq, pos)
if (offset != -1) {
let consumed = queue.substring(pos, offset-pos)
@ -203,25 +202,24 @@ open class TokenQueue
return ""
}
open func consumeToIgnoreCase(_ seq: String)->String {
open func consumeToIgnoreCase(_ seq: String) -> String {
let start = pos
let first = seq.substring(0, 1)
let canScan = first.lowercased() == first.uppercased() // if first is not cased, use index of
while (!isEmpty()) {
if (matches(seq)){
if (matches(seq)) {
break
}
if (canScan)
{
if (canScan) {
let skip = queue.indexOf(first, pos) - pos
if (skip == 0){ // this char is the skip char, but not match, so force advance of pos
if (skip == 0) { // this char is the skip char, but not match, so force advance of pos
pos+=1
}else if (skip < 0){ // no chance of finding, grab to end
} else if (skip < 0) { // no chance of finding, grab to end
pos = queue.characters.count
}else{
} else {
pos += skip
}
} else{
} else {
pos+=1
}
}
@ -236,10 +234,10 @@ open class TokenQueue
*/
// todo: method name. not good that consumeTo cares for case, and consume to any doesn't. And the only use for this
// is is a case sensitive time...
open func consumeToAny(_ seq: String...)->String {
open func consumeToAny(_ seq: String...) -> String {
return consumeToAny(seq)
}
open func consumeToAny(_ seq: [String])->String {
open func consumeToAny(_ seq: [String]) -> String {
let start = pos
while (!isEmpty() && !matchesAny(seq)) {
pos+=1
@ -255,13 +253,13 @@ open class TokenQueue
* @param seq String to match up to, and not include in return, and to pull off queue. <b>Case sensitive.</b>
* @return Data matched from queue.
*/
open func chompTo(_ seq: String)->String {
open func chompTo(_ seq: String) -> String {
let data = consumeTo(seq)
matchChomp(seq)
return data
}
open func chompToIgnoreCase(_ seq: String)->String {
open func chompToIgnoreCase(_ seq: String) -> String {
let data = consumeToIgnoreCase(seq) // case insensitive scan
matchChomp(seq)
return data
@ -276,35 +274,34 @@ open class TokenQueue
* @param close closer
* @return data matched from the queue
*/
open func chompBalanced(_ open:Character, _ close: Character)->String {
open func chompBalanced(_ open: Character, _ close: Character) -> String {
var start = -1
var end = -1
var depth = 0
var last : Character = Character(UnicodeScalar(0))
var last: Character = Character(UnicodeScalar(0))
var inQuote = false
repeat {
if (isEmpty()){break}
if (isEmpty()) {break}
let c = consume()
if (last.unicodeScalar.value == 0 || last != TokenQueue.ESC) {
if ((c=="'" || c=="\"") && c != open){
if ((c=="'" || c=="\"") && c != open) {
inQuote = !inQuote
}
if (inQuote){
if (inQuote) {
continue
}
if (c==open) {
depth+=1
if (start == -1){
if (start == -1) {
start = pos
}
}
else if (c==close){
} else if (c==close) {
depth-=1
}
}
if (depth > 0 && last.unicodeScalar.value != 0){
if (depth > 0 && last.unicodeScalar.value != 0) {
end = pos // don't include the outer match pair in the return
}
last = c
@ -317,17 +314,15 @@ open class TokenQueue
* @param in backslash escaped string
* @return unescaped string
*/
open static func unescape(_ input: String)->String {
open static func unescape(_ input: String) -> String {
let out = StringBuilder()
var last = Character(UnicodeScalar(0))
for c in input.characters
{
for c in input.characters {
if (c == ESC) {
if (last.unicodeScalar.value != 0 && last == TokenQueue.ESC){
if (last.unicodeScalar.value != 0 && last == TokenQueue.ESC) {
out.append(c)
}
}
else{
} else {
out.append(c)
}
last = c
@ -340,7 +335,7 @@ open class TokenQueue
* @return Whether consuming whitespace or not
*/
@discardableResult
open func consumeWhitespace()->Bool {
open func consumeWhitespace() -> Bool {
var seen = false
while (matchesWhitespace()) {
pos+=1
@ -354,9 +349,9 @@ open class TokenQueue
* @return String of word characters from queue, or empty string if none.
*/
@discardableResult
open func consumeWord()->String {
open func consumeWord() -> String {
let start = pos
while (matchesWord()){
while (matchesWord()) {
pos+=1
}
return queue.substring(start, pos-start)
@ -367,9 +362,9 @@ open class TokenQueue
*
* @return tag name
*/
open func consumeTagName()->String {
open func consumeTagName() -> String {
let start = pos
while (!isEmpty() && (matchesWord() || matchesAny(":", "_", "-"))){
while (!isEmpty() && (matchesWord() || matchesAny(":", "_", "-"))) {
pos+=1
}
@ -381,9 +376,9 @@ open class TokenQueue
*
* @return tag name
*/
open func consumeElementSelector()->String {
open func consumeElementSelector() -> String {
let start = pos
while (!isEmpty() && (matchesWord() || matchesAny("*|","|", "_", "-"))){
while (!isEmpty() && (matchesWord() || matchesAny("*|", "|", "_", "-"))) {
pos+=1
}
@ -395,9 +390,9 @@ open class TokenQueue
http://www.w3.org/TR/CSS2/syndata.html#value-def-identifier
@return identifier
*/
open func consumeCssIdentifier()->String {
open func consumeCssIdentifier() -> String {
let start = pos
while (!isEmpty() && (matchesWord() || matchesAny("-", "_"))){
while (!isEmpty() && (matchesWord() || matchesAny("-", "_"))) {
pos+=1
}
@ -408,9 +403,9 @@ open class TokenQueue
Consume an attribute key off the queue (letter, digit, -, _, :")
@return attribute key
*/
open func consumeAttributeKey()->String {
open func consumeAttributeKey() -> String {
let start = pos
while (!isEmpty() && (matchesWord() || matchesAny("-", "_", ":"))){
while (!isEmpty() && (matchesWord() || matchesAny("-", "_", ":"))) {
pos+=1
}
@ -421,14 +416,13 @@ open class TokenQueue
Consume and return whatever is left on the queue.
@return remained of queue.
*/
open func remainder()->String {
open func remainder() -> String {
let remainder = queue.substring(pos, queue.characters.count-pos)
pos = queue.characters.count
return remainder
}
open func toString()->String {
open func toString() -> String {
return queue.substring(pos)
}
}

View File

@ -8,23 +8,22 @@
import Foundation
final class Tokeniser
{
static let replacementChar : UnicodeScalar = "\u{FFFD}" // replaces null character
private static let notCharRefCharsSorted : [UnicodeScalar] = ["\t", "\n", "\r",UnicodeScalar.BackslashF, " ", "<", "&"].sorted()
final class Tokeniser {
static let replacementChar: UnicodeScalar = "\u{FFFD}" // replaces null character
private static let notCharRefCharsSorted: [UnicodeScalar] = ["\t", "\n", "\r", UnicodeScalar.BackslashF, " ", "<", "&"].sorted()
private let reader : CharacterReader // html input
private let errors : ParseErrorList? // errors found while tokenising
private let reader: CharacterReader // html input
private let errors: ParseErrorList? // errors found while tokenising
private var state: TokeniserState = TokeniserState.Data // current tokenisation state
private var emitPending: Token? // the token we are about to emit on next read
private var isEmitPending : Bool = false
private var charsString : String? = nil // characters pending an emit. Will fall to charsBuilder if more than one
private let charsBuilder : StringBuilder = StringBuilder(1024) // buffers characters to output as one token, if more than one emit per read
let dataBuffer : StringBuilder = StringBuilder(1024) // buffers data looking for </script>
private var isEmitPending: Bool = false
private var charsString: String? = nil // characters pending an emit. Will fall to charsBuilder if more than one
private let charsBuilder: StringBuilder = StringBuilder(1024) // buffers characters to output as one token, if more than one emit per read
let dataBuffer: StringBuilder = StringBuilder(1024) // buffers data looking for </script>
var tagPending : Token.Tag = Token.Tag() // tag we are building up
let startPending : Token.StartTag = Token.StartTag()
var tagPending: Token.Tag = Token.Tag() // tag we are building up
let startPending: Token.StartTag = Token.StartTag()
let endPending: Token.EndTag = Token.EndTag()
let charPending: Token.Char = Token.Char()
let doctypePending: Token.Doctype = Token.Doctype() // doctype building up
@ -32,7 +31,6 @@ final class Tokeniser
private var lastStartTag: String? // the last start tag emitted, to test appropriate end tag
private var selfClosingFlagAcknowledged: Bool = true
init(_ reader: CharacterReader, _ errors: ParseErrorList?) {
self.reader = reader
self.errors = errors
@ -44,7 +42,7 @@ final class Tokeniser
selfClosingFlagAcknowledged = true
}
while (!isEmitPending){
while (!isEmitPending) {
try state.read(self, reader)
}
@ -55,7 +53,7 @@ final class Tokeniser
charsString = nil
return charPending.data(str)
} else if (charsString != nil) {
let token : Token = charPending.data(charsString!)
let token: Token = charPending.data(charsString!)
charsString = nil
return token
} else {
@ -71,14 +69,14 @@ final class Tokeniser
isEmitPending = true
if (token.type == Token.TokenType.StartTag) {
let startTag : Token.StartTag = token as! Token.StartTag
let startTag: Token.StartTag = token as! Token.StartTag
lastStartTag = startTag._tagName!
if (startTag._selfClosing){
if (startTag._selfClosing) {
selfClosingFlagAcknowledged = false
}
} else if (token.type == Token.TokenType.EndTag) {
let endTag : Token.EndTag = token as! Token.EndTag
if (endTag._attributes.size() != 0){
let endTag: Token.EndTag = token as! Token.EndTag
if (endTag._attributes.size() != 0) {
error("Attributes incorrectly present on end tag")
}
}
@ -89,8 +87,7 @@ final class Tokeniser
// does not set isEmitPending; read checks that
if (charsString == nil) {
charsString = str
}
else {
} else {
if (charsBuilder.length == 0) { // switching to string builder as more than one emit before read
charsBuilder.append(charsString!)
}
@ -99,7 +96,7 @@ final class Tokeniser
}
func emit(_ chars: [UnicodeScalar]) {
emit(String(chars.map{Character($0)}))
emit(String(chars.map {Character($0)}))
}
// func emit(_ codepoints: [Int]) {
@ -110,7 +107,7 @@ final class Tokeniser
emit(String(c))
}
func getState()->TokeniserState {
func getState() -> TokeniserState {
return state
}
@ -128,16 +125,16 @@ final class Tokeniser
}
private var codepointHolder: [UnicodeScalar] = [UnicodeScalar(0)!] // holder to not have to keep creating arrays
private var multipointHolder: [UnicodeScalar] = [UnicodeScalar(0)!,UnicodeScalar(0)!]
private var multipointHolder: [UnicodeScalar] = [UnicodeScalar(0)!, UnicodeScalar(0)!]
func consumeCharacterReference(_ additionalAllowedCharacter: UnicodeScalar?, _ inAttribute: Bool)throws->[UnicodeScalar]? {
if (reader.isEmpty()){
if (reader.isEmpty()) {
return nil
}
if (additionalAllowedCharacter != nil && additionalAllowedCharacter == reader.current()){
if (additionalAllowedCharacter != nil && additionalAllowedCharacter == reader.current()) {
return nil
}
if (reader.matchesAnySorted(Tokeniser.notCharRefCharsSorted)){
if (reader.matchesAnySorted(Tokeniser.notCharRefCharsSorted)) {
return nil
}
@ -151,18 +148,16 @@ final class Tokeniser
reader.rewindToMark()
return nil
}
if (!reader.matchConsume(";")){
if (!reader.matchConsume(";")) {
characterReferenceError("missing semicolon") // missing semi
}
var charval : Int = -1
var charval: Int = -1
let base: Int = isHexMode ? 16 : 10
if let num = Int(numRef,radix: base)
{
if let num = Int(numRef, radix: base) {
charval = num
}
if (charval == -1 || (charval >= 0xD800 && charval <= 0xDFFF) || charval > 0x10FFFF) {
characterReferenceError("character outside of valid range")
codeRef[0] = Tokeniser.replacementChar
@ -175,14 +170,14 @@ final class Tokeniser
}
} else { // named
// get as many letters as possible, and look for matching entities.
let nameRef : String = reader.consumeLetterThenDigitSequence()
let nameRef: String = reader.consumeLetterThenDigitSequence()
let looksLegit: Bool = reader.matches(";")
// found if a base named entity without a ;, or an extended entity with the ;.
let found: Bool = (Entities.isBaseNamedEntity(nameRef) || (Entities.isNamedEntity(nameRef) && looksLegit))
if (!found) {
reader.rewindToMark()
if (looksLegit){ // named with semicolon
if (looksLegit) { // named with semicolon
characterReferenceError("invalid named referenece '\(nameRef)'")
}
return nil
@ -192,7 +187,7 @@ final class Tokeniser
reader.rewindToMark()
return nil
}
if (!reader.matchConsume(";")){
if (!reader.matchConsume(";")) {
characterReferenceError("missing semicolon") // missing semi
}
let numChars: Int = Entities.codepointsForName(nameRef, codepoints: &multipointHolder)
@ -240,45 +235,45 @@ final class Tokeniser
}
func isAppropriateEndTagToken()throws->Bool {
if(lastStartTag != nil){
if(lastStartTag != nil) {
let s = try tagPending.name()
return s.equalsIgnoreCase(string: lastStartTag!)
}
return false
}
func appropriateEndTagName()->String? {
if (lastStartTag == nil){
func appropriateEndTagName() -> String? {
if (lastStartTag == nil) {
return nil
}
return lastStartTag
}
func error(_ state: TokeniserState) {
if (errors != nil && errors!.canAddError()){
if (errors != nil && errors!.canAddError()) {
errors?.add(ParseError(reader.getPos(), "Unexpected character '\(String(reader.current()))' in input state [\(state.description)]"))
}
}
func eofError(_ state: TokeniserState) {
if (errors != nil && errors!.canAddError()){
if (errors != nil && errors!.canAddError()) {
errors?.add(ParseError(reader.getPos(), "Unexpectedly reached end of file (EOF) in input state [\(state.description)]"))
}
}
private func characterReferenceError(_ message: String) {
if (errors != nil && errors!.canAddError()){
if (errors != nil && errors!.canAddError()) {
errors?.add(ParseError(reader.getPos(), "Invalid character reference: \(message)"))
}
}
private func error(_ errorMsg: String) {
if (errors != nil && errors!.canAddError()){
if (errors != nil && errors!.canAddError()) {
errors?.add(ParseError(reader.getPos(), errorMsg))
}
}
func currentNodeInHtmlNS()->Bool {
func currentNodeInHtmlNS() -> Bool {
// todo: implement namespaces correctly
return true
// Element currentNode = currentNode()
@ -291,22 +286,21 @@ final class Tokeniser
* @return unescaped string from reader
*/
func unescapeEntities(_ inAttribute: Bool)throws->String {
let builder : StringBuilder = StringBuilder()
let builder: StringBuilder = StringBuilder()
while (!reader.isEmpty()) {
builder.append(reader.consumeTo("&"))
if (reader.matches("&")) {
reader.consume()
if let c = try consumeCharacterReference(nil, inAttribute)
{
if (c.count==0){
if let c = try consumeCharacterReference(nil, inAttribute) {
if (c.count==0) {
builder.append("&")
}else {
} else {
builder.appendCodePoint(c[0])
if (c.count == 2){
if (c.count == 2) {
builder.appendCodePoint(c[1])
}
}
}else {
} else {
builder.append("&")
}
}
@ -314,7 +308,4 @@ final class Tokeniser
return builder.toString()
}
}

View File

@ -12,22 +12,20 @@ protocol TokeniserStateProtocol {
func read(_ t: Tokeniser, _ r: CharacterReader)throws
}
public class TokeniserStateVars
{
public static let nullScalr : UnicodeScalar = "\u{0000}"
public class TokeniserStateVars {
public static let nullScalr: UnicodeScalar = "\u{0000}"
static let attributeSingleValueCharsSorted = ["'", "&", nullScalr].sorted()
static let attributeDoubleValueCharsSorted = ["\"", "&", nullScalr].sorted()
static let attributeNameCharsSorted = ["\t", "\n", "\r", UnicodeScalar.BackslashF, " ", "/", "=", ">", nullScalr, "\"", "'", "<"].sorted()
static let attributeValueUnquoted = ["\t", "\n", "\r", UnicodeScalar.BackslashF, " ", "&", ">", nullScalr, "\"", "'", "<", "=", "`"].sorted()
static let replacementChar : UnicodeScalar = Tokeniser.replacementChar
static let replacementStr : String = String(Tokeniser.replacementChar)
static let eof : UnicodeScalar = CharacterReader.EOF
static let replacementChar: UnicodeScalar = Tokeniser.replacementChar
static let replacementStr: String = String(Tokeniser.replacementChar)
static let eof: UnicodeScalar = CharacterReader.EOF
}
enum TokeniserState: TokeniserStateProtocol
{
enum TokeniserState: TokeniserStateProtocol {
case Data
case CharacterReferenceInData
case Rcdata
@ -96,10 +94,8 @@ enum TokeniserState: TokeniserStateProtocol
case BogusDoctype
case CdataSection
internal func read(_ t: Tokeniser, _ r: CharacterReader)throws
{
switch self
{
internal func read(_ t: Tokeniser, _ r: CharacterReader)throws {
switch self {
case .Data:
switch (r.current()) {
case "&":
@ -286,8 +282,7 @@ enum TokeniserState: TokeniserStateProtocol
return
}
func anythingElse(_ t: Tokeniser, _ r: CharacterReader)
{
func anythingElse(_ t: Tokeniser, _ r: CharacterReader) {
t.emit("</" + t.dataBuffer.toString())
r.unconsume()
t.transition(.Rcdata)
@ -296,44 +291,44 @@ enum TokeniserState: TokeniserStateProtocol
let c = r.consume()
switch (c) {
case "\t":
if (try t.isAppropriateEndTagToken()){
if (try t.isAppropriateEndTagToken()) {
t.transition(.BeforeAttributeName)
}else{
} else {
anythingElse(t, r)
}
break
case "\n":
if (try t.isAppropriateEndTagToken()){
if (try t.isAppropriateEndTagToken()) {
t.transition(.BeforeAttributeName)
}else{
} else {
anythingElse(t, r)
}
break
case "\r":
if (try t.isAppropriateEndTagToken()){
if (try t.isAppropriateEndTagToken()) {
t.transition(.BeforeAttributeName)
}else{
} else {
anythingElse(t, r)
}
break
case UnicodeScalar.BackslashF:
if (try t.isAppropriateEndTagToken()){
if (try t.isAppropriateEndTagToken()) {
t.transition(.BeforeAttributeName)
}else{
} else {
anythingElse(t, r)
}
break
case " ":
if (try t.isAppropriateEndTagToken()){
if (try t.isAppropriateEndTagToken()) {
t.transition(.BeforeAttributeName)
}else{
} else {
anythingElse(t, r)
}
break
case "/":
if (try t.isAppropriateEndTagToken()){
if (try t.isAppropriateEndTagToken()) {
t.transition(.SelfClosingStartTag)
}else{
} else {
anythingElse(t, r)
}
break
@ -341,8 +336,7 @@ enum TokeniserState: TokeniserStateProtocol
if (try t.isAppropriateEndTagToken()) {
try t.emitTagPending()
t.transition(.Data)
}
else{anythingElse(t, r)}
} else {anythingElse(t, r)}
break
default:
anythingElse(t, r)
@ -601,7 +595,7 @@ enum TokeniserState: TokeniserStateProtocol
}
break
case .ScriptDataDoubleEscapeEnd:
TokeniserState.handleDataDoubleEscapeTag(t,r, .ScriptDataEscaped, .ScriptDataDoubleEscaped)
TokeniserState.handleDataDoubleEscapeTag(t, r, .ScriptDataEscaped, .ScriptDataDoubleEscaped)
break
case .BeforeAttributeName:
// from tagname <xxx
@ -724,7 +718,7 @@ enum TokeniserState: TokeniserStateProtocol
case .AfterAttributeName:
let c = r.consume()
switch (c) {
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
// ignore
break
case "/":
@ -746,7 +740,7 @@ enum TokeniserState: TokeniserStateProtocol
t.eofError(self)
t.transition(.Data)
break
case "\"","'","<":
case "\"", "'", "<":
t.error(self)
try t.tagPending.newAttribute()
t.tagPending.appendAttributeName(c)
@ -761,7 +755,7 @@ enum TokeniserState: TokeniserStateProtocol
case .BeforeAttributeValue:
let c = r.consume()
switch (c) {
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
// ignore
break
case "\"":
@ -789,7 +783,7 @@ enum TokeniserState: TokeniserStateProtocol
try t.emitTagPending()
t.transition(.Data)
break
case "<","=","`":
case "<", "=", "`":
t.error(self)
t.tagPending.appendAttributeValue(c)
t.transition(.AttributeValue_unquoted)
@ -801,9 +795,9 @@ enum TokeniserState: TokeniserStateProtocol
break
case .AttributeValue_doubleQuoted:
let value = r.consumeToAny(TokeniserStateVars.attributeDoubleValueCharsSorted)
if (value.characters.count > 0){
if (value.characters.count > 0) {
t.tagPending.appendAttributeValue(value)
}else{
} else {
t.tagPending.setEmptyAttributeValue()
}
@ -814,9 +808,9 @@ enum TokeniserState: TokeniserStateProtocol
break
case "&":
if let ref = try t.consumeCharacterReference("\"", true){
if let ref = try t.consumeCharacterReference("\"", true) {
t.tagPending.appendAttributeValue(ref)
}else{
} else {
t.tagPending.appendAttributeValue("&")
}
break
@ -835,9 +829,9 @@ enum TokeniserState: TokeniserStateProtocol
break
case .AttributeValue_singleQuoted:
let value = r.consumeToAny(TokeniserStateVars.attributeSingleValueCharsSorted)
if (value.characters.count > 0){
if (value.characters.count > 0) {
t.tagPending.appendAttributeValue(value)
}else{
} else {
t.tagPending.setEmptyAttributeValue()
}
@ -848,9 +842,9 @@ enum TokeniserState: TokeniserStateProtocol
break
case "&":
if let ref = try t.consumeCharacterReference("'", true){
if let ref = try t.consumeCharacterReference("'", true) {
t.tagPending.appendAttributeValue(ref)
}else{
} else {
t.tagPending.appendAttributeValue("&")
}
break
@ -869,19 +863,19 @@ enum TokeniserState: TokeniserStateProtocol
break
case .AttributeValue_unquoted:
let value = r.consumeToAnySorted(TokeniserStateVars.attributeValueUnquoted)
if (value.characters.count > 0){
if (value.characters.count > 0) {
t.tagPending.appendAttributeValue(value)
}
let c = r.consume()
switch (c) {
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
t.transition(.BeforeAttributeName)
break
case "&":
if let ref = try t.consumeCharacterReference(">", true){
if let ref = try t.consumeCharacterReference(">", true) {
t.tagPending.appendAttributeValue(ref)
}else{
} else {
t.tagPending.appendAttributeValue("&")
}
break
@ -897,7 +891,7 @@ enum TokeniserState: TokeniserStateProtocol
t.eofError(self)
t.transition(.Data)
break
case "\"","'","<","=","`":
case "\"", "'", "<", "=", "`":
t.error(self)
t.tagPending.appendAttributeValue(c)
break
@ -910,7 +904,7 @@ enum TokeniserState: TokeniserStateProtocol
// CharacterReferenceInAttributeValue state handled inline
let c = r.consume()
switch (c) {
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
t.transition(.BeforeAttributeName)
break
case "/":
@ -952,7 +946,7 @@ enum TokeniserState: TokeniserStateProtocol
// todo: handle bogus comment starting from eof. when does that trigger?
// rewind to capture character that lead us here
r.unconsume()
let comment : Token.Comment = Token.Comment()
let comment: Token.Comment = Token.Comment()
comment.bogus = true
comment.data.append(r.consumeTo(">"))
// todo: replace nullChar with replaceChar
@ -1128,7 +1122,7 @@ enum TokeniserState: TokeniserStateProtocol
case .Doctype:
let c = r.consume()
switch (c) {
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
t.transition(.BeforeDoctypeName)
break
case TokeniserStateVars.eof:
@ -1154,7 +1148,7 @@ enum TokeniserState: TokeniserStateProtocol
}
let c = r.consume()
switch (c) {
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
break // ignore whitespace
case TokeniserStateVars.nullScalr:
t.error(self)
@ -1187,7 +1181,7 @@ enum TokeniserState: TokeniserStateProtocol
try t.emitDoctypePending()
t.transition(.Data)
break
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
t.transition(.AfterDoctypeName)
break
case TokeniserStateVars.nullScalr:
@ -1212,9 +1206,9 @@ enum TokeniserState: TokeniserStateProtocol
t.transition(.Data)
return
}
if (r.matchesAny("\t", "\n", "\r", UnicodeScalar.BackslashF, " ")){
if (r.matchesAny("\t", "\n", "\r", UnicodeScalar.BackslashF, " ")) {
r.advance() // ignore whitespace
}else if (r.matches(">")) {
} else if (r.matches(">")) {
try t.emitDoctypePending()
t.advanceTransition(.Data)
} else if (r.matchConsumeIgnoreCase("PUBLIC")) {
@ -1230,7 +1224,7 @@ enum TokeniserState: TokeniserStateProtocol
case .AfterDoctypePublicKeyword:
let c = r.consume()
switch (c) {
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
t.transition(.BeforeDoctypePublicIdentifier)
break
case "\"":
@ -1264,7 +1258,7 @@ enum TokeniserState: TokeniserStateProtocol
case .BeforeDoctypePublicIdentifier:
let c = r.consume()
switch (c) {
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
break
case "\"":
// set public id to empty string
@ -1347,7 +1341,7 @@ enum TokeniserState: TokeniserStateProtocol
case .AfterDoctypePublicIdentifier:
let c = r.consume()
switch (c) {
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
t.transition(.BetweenDoctypePublicAndSystemIdentifiers)
break
case ">":
@ -1379,7 +1373,7 @@ enum TokeniserState: TokeniserStateProtocol
case .BetweenDoctypePublicAndSystemIdentifiers:
let c = r.consume()
switch (c) {
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
break
case ">":
try t.emitDoctypePending()
@ -1410,7 +1404,7 @@ enum TokeniserState: TokeniserStateProtocol
case .AfterDoctypeSystemKeyword:
let c = r.consume()
switch (c) {
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
t.transition(.BeforeDoctypeSystemIdentifier)
break
case ">":
@ -1444,7 +1438,7 @@ enum TokeniserState: TokeniserStateProtocol
case .BeforeDoctypeSystemIdentifier:
let c = r.consume()
switch (c) {
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
break
case "\"":
// set system id to empty string
@ -1527,7 +1521,7 @@ enum TokeniserState: TokeniserStateProtocol
case .AfterDoctypeSystemIdentifier:
let c = r.consume()
switch (c) {
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
break
case ">":
try t.emitDoctypePending()
@ -1570,14 +1564,6 @@ enum TokeniserState: TokeniserStateProtocol
}
}
var description: String {return String(describing: type(of: self))}
/**
* Handles RawtextEndTagName, ScriptDataEndTagName, and ScriptDataEscapedEndTagName. Same body impl, just
@ -1595,7 +1581,7 @@ enum TokeniserState: TokeniserStateProtocol
if (try t.isAppropriateEndTagToken() && !r.isEmpty()) {
let c = r.consume()
switch (c) {
case "\t","\n","\r",UnicodeScalar.BackslashF," ":
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ":
t.transition(BeforeAttributeName)
break
case "/":
@ -1641,9 +1627,9 @@ enum TokeniserState: TokeniserStateProtocol
private static func readCharRef(_ t: Tokeniser, _ advance: TokeniserState)throws {
let c = try t.consumeCharacterReference(nil, false)
if (c == nil){
if (c == nil) {
t.emit("&")
}else{
} else {
t.emit(c!)
}
t.transition(advance)
@ -1669,10 +1655,10 @@ enum TokeniserState: TokeniserStateProtocol
let c = r.consume()
switch (c) {
case "\t","\n","\r",UnicodeScalar.BackslashF," ","/",">":
if (t.dataBuffer.toString() == "script"){
case "\t", "\n", "\r", UnicodeScalar.BackslashF, " ", "/", ">":
if (t.dataBuffer.toString() == "script") {
t.transition(primary)
}else{
} else {
t.transition(fallback)
}
t.emit(c)
@ -1683,7 +1669,4 @@ enum TokeniserState: TokeniserStateProtocol
}
}
}

View File

@ -21,17 +21,16 @@ public class TreeBuilder {
private let start: Token.StartTag = Token.StartTag() // start tag to process
private let end: Token.EndTag = Token.EndTag()
public func defaultSettings()->ParseSettings{preconditionFailure("This method must be overridden")}
public func defaultSettings() -> ParseSettings {preconditionFailure("This method must be overridden")}
public init() {
doc = Document("")
reader = CharacterReader("")
tokeniser = Tokeniser(reader,nil)
tokeniser = Tokeniser(reader, nil)
stack = Array<Element>()
baseUri = ""
errors = ParseErrorList(0,0)
settings = ParseSettings(false,false)
errors = ParseErrorList(0, 0)
settings = ParseSettings(false, false)
}
public func initialiseParse(_ input: String, _ baseUri: String, _ errors: ParseErrorList, _ settings: ParseSettings) {
@ -44,7 +43,6 @@ public class TreeBuilder {
self.baseUri = baseUri
}
func parse(_ input: String, _ baseUri: String, _ errors: ParseErrorList, _ settings: ParseSettings)throws->Document {
initialiseParse(input, baseUri, errors, settings)
try runParser()
@ -57,14 +55,14 @@ public class TreeBuilder {
try process(token)
token.reset()
if (token.type == Token.TokenType.EOF){
if (token.type == Token.TokenType.EOF) {
break
}
}
}
@discardableResult
public func process(_ token: Token)throws->Bool{preconditionFailure("This method must be overridden")}
public func process(_ token: Token)throws->Bool {preconditionFailure("This method must be overridden")}
@discardableResult
public func processStartTag(_ name: String)throws->Bool {
@ -93,8 +91,7 @@ public class TreeBuilder {
return try process(end.reset().name(name))
}
public func currentElement()->Element? {
public func currentElement() -> Element? {
let size: Int = stack.count
return size > 0 ? stack[size-1] : nil
}

View File

@ -15,9 +15,8 @@ private let alphaNumericSet = CharacterSet.alphanumerics
private let symbolSet = CharacterSet.symbols
private let digitSet = CharacterSet.decimalDigits
extension UnicodeScalar
{
public static let BackslashF : UnicodeScalar = UnicodeScalar(12)
extension UnicodeScalar {
public static let BackslashF: UnicodeScalar = UnicodeScalar(12)
func isMemberOfCharacterSet(_ set: CharacterSet) -> Bool {
return set.contains(self)
@ -43,7 +42,7 @@ extension UnicodeScalar
switch self {
case " ", "\t", "\n", "\r" ,UnicodeScalar.BackslashF: return true
case " ", "\t", "\n", "\r", UnicodeScalar.BackslashF: return true
case "\u{000C}", "\u{000B}", "\u{0085}": return true // Form Feed, vertical tab, next line (nel)

View File

@ -8,15 +8,14 @@
import Foundation
struct Validate
{
struct Validate {
/**
* Validates that the object is not null
* @param obj object to test
*/
public static func notNull(obj:Any?) throws {
if (obj == nil){
if (obj == nil) {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Object must not be null")
}
}
@ -26,8 +25,8 @@ struct Validate
* @param obj object to test
* @param msg message to output if validation fails
*/
public static func notNull(obj:AnyObject?, msg:String) throws {
if (obj == nil){
public static func notNull(obj: AnyObject?, msg: String) throws {
if (obj == nil) {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: msg)
}
}
@ -36,8 +35,8 @@ struct Validate
* Validates that the value is true
* @param val object to test
*/
public static func isTrue(val:Bool) throws {
if (!val){
public static func isTrue(val: Bool) throws {
if (!val) {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Must be true")
}
}
@ -47,8 +46,8 @@ struct Validate
* @param val object to test
* @param msg message to output if validation fails
*/
public static func isTrue(val: Bool ,msg: String) throws {
if (!val){
public static func isTrue(val: Bool, msg: String) throws {
if (!val) {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: msg)
}
}
@ -58,7 +57,7 @@ struct Validate
* @param val object to test
*/
public static func isFalse(val: Bool) throws {
if (val){
if (val) {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "Must be false")
}
}
@ -69,7 +68,7 @@ struct Validate
* @param msg message to output if validation fails
*/
public static func isFalse(val: Bool, msg: String) throws {
if (val){
if (val) {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: msg)
}
}
@ -79,7 +78,7 @@ struct Validate
* @param objects the array to test
*/
public static func noNullElements(objects: [AnyObject?]) throws {
try noNullElements(objects: objects, msg: "Array must not contain any null objects");
try noNullElements(objects: objects, msg: "Array must not contain any null objects")
}
/**
@ -89,7 +88,7 @@ struct Validate
*/
public static func noNullElements(objects: [AnyObject?], msg: String) throws {
for obj in objects {
if (obj == nil){
if (obj == nil) {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: msg)
}
}
@ -99,9 +98,8 @@ struct Validate
* Validates that the string is not empty
* @param string the string to test
*/
public static func notEmpty(string: String?) throws
{
if (string == nil || string?.characters.count == 0){
public static func notEmpty(string: String?) throws {
if (string == nil || string?.characters.count == 0) {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: "String must not be empty")
}
@ -113,7 +111,7 @@ struct Validate
* @param msg message to output if validation fails
*/
public static func notEmpty(string: String?, msg: String ) throws {
if (string == nil || string?.characters.count == 0){
if (string == nil || string?.characters.count == 0) {
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: msg)
}
}
@ -126,7 +124,6 @@ struct Validate
throw Exception.Error(type: ExceptionType.IllegalArgumentException, Message: msg)
}
/**
Helper
*/

View File

@ -57,21 +57,19 @@
import Foundation
public class Whitelist {
private var tagNames : Set<TagName> // tags allowed, lower case. e.g. [p, br, span]
private var attributes : Dictionary<TagName, Set<AttributeKey>> // tag -> attribute[]. allowed attributes [href] for a tag.
private var enforcedAttributes : Dictionary<TagName, Dictionary<AttributeKey, AttributeValue>> // always set these attribute values
private var protocols : Dictionary<TagName, Dictionary<AttributeKey, Set<Protocol>>> // allowed URL protocols for attributes
private var preserveRelativeLinks : Bool // option to preserve relative links
private var tagNames: Set<TagName> // tags allowed, lower case. e.g. [p, br, span]
private var attributes: Dictionary<TagName, Set<AttributeKey>> // tag -> attribute[]. allowed attributes [href] for a tag.
private var enforcedAttributes: Dictionary<TagName, Dictionary<AttributeKey, AttributeValue>> // always set these attribute values
private var protocols: Dictionary<TagName, Dictionary<AttributeKey, Set<Protocol>>> // allowed URL protocols for attributes
private var preserveRelativeLinks: Bool // option to preserve relative links
/**
This whitelist allows only text nodes: all HTML will be stripped.
@return whitelist
*/
public static func none()->Whitelist {
public static func none() -> Whitelist {
return Whitelist()
}
@ -172,10 +170,6 @@ public class Whitelist {
.addProtocols("q", "cite", "http", "https")
}
/**
Create a new, empty whitelist. Generally it will be better to start with a default prepared whitelist instead.
@ -200,8 +194,7 @@ public class Whitelist {
*/
@discardableResult
open func addTags(_ tags: String...)throws ->Whitelist {
for tagName in tags
{
for tagName in tags {
try Validate.notEmpty(string: tagName)
tagNames.insert(TagName.valueOf(tagName))
}
@ -220,7 +213,7 @@ public class Whitelist {
for tag in tags {
try Validate.notEmpty(string: tag)
let tagName : TagName = TagName.valueOf(tag)
let tagName: TagName = TagName.valueOf(tag)
if(tagNames.contains(tagName)) { // Only look in sub-maps if tag was allowed
tagNames.remove(tagName)
@ -253,19 +246,17 @@ public class Whitelist {
try Validate.isTrue(val: keys.count > 0, msg: "No attributes supplied.")
let tagName = TagName.valueOf(tag)
if (!tagNames.contains(tagName)){
if (!tagNames.contains(tagName)) {
tagNames.insert(tagName)
}
var attributeSet = Set<AttributeKey>()
for key in keys
{
for key in keys {
try Validate.notEmpty(string: key)
attributeSet.insert(AttributeKey.valueOf(key))
}
if var currentSet = attributes[tagName]
{
for at in attributeSet{
if var currentSet = attributes[tagName] {
for at in attributeSet {
currentSet.insert(at)
}
attributes[tagName] = currentSet
@ -296,39 +287,34 @@ public class Whitelist {
try Validate.notEmpty(string: tag)
try Validate.isTrue(val: keys.count > 0, msg: "No attributes supplied.")
let tagName : TagName = TagName.valueOf(tag)
let tagName: TagName = TagName.valueOf(tag)
var attributeSet = Set<AttributeKey>()
for key in keys {
try Validate.notEmpty(string: key)
attributeSet.insert(AttributeKey.valueOf(key))
}
if(tagNames.contains(tagName)) { // Only look in sub-maps if tag was allowed
if var currentSet = attributes[tagName]
{
for l in attributeSet
{
if var currentSet = attributes[tagName] {
for l in attributeSet {
currentSet.remove(l)
}
attributes[tagName] = currentSet
if(currentSet.isEmpty){ // Remove tag from attribute map if no attributes are allowed for tag
if(currentSet.isEmpty) { // Remove tag from attribute map if no attributes are allowed for tag
attributes.removeValue(forKey: tagName)
}
}
}
if(tag == ":all"){ // Attribute needs to be removed from all individually set tags
for name in attributes.keys
{
var currentSet : Set<AttributeKey> = attributes[name]!
for l in attributeSet{
if(tag == ":all") { // Attribute needs to be removed from all individually set tags
for name in attributes.keys {
var currentSet: Set<AttributeKey> = attributes[name]!
for l in attributeSet {
currentSet.remove(l)
}
attributes[name] = currentSet
if(currentSet.isEmpty){ // Remove tag from attribute map if no attributes are allowed for tag
if(currentSet.isEmpty) { // Remove tag from attribute map if no attributes are allowed for tag
attributes.removeValue(forKey: name)
}
}
@ -355,17 +341,17 @@ public class Whitelist {
try Validate.notEmpty(string: key)
try Validate.notEmpty(string: value)
let tagName : TagName = TagName.valueOf(tag)
if (!tagNames.contains(tagName)){
let tagName: TagName = TagName.valueOf(tag)
if (!tagNames.contains(tagName)) {
tagNames.insert(tagName)
}
let attrKey : AttributeKey = AttributeKey.valueOf(key)
let attrVal : AttributeValue = AttributeValue.valueOf(value)
let attrKey: AttributeKey = AttributeKey.valueOf(key)
let attrVal: AttributeValue = AttributeValue.valueOf(value)
if (enforcedAttributes[tagName] != nil) {
enforcedAttributes[tagName]?[attrKey] = attrVal
} else {
var attrMap : Dictionary<AttributeKey, AttributeValue> = Dictionary<AttributeKey, AttributeValue>()
var attrMap: Dictionary<AttributeKey, AttributeValue> = Dictionary<AttributeKey, AttributeValue>()
attrMap[attrKey] = attrVal
enforcedAttributes[tagName] = attrMap
}
@ -384,14 +370,14 @@ public class Whitelist {
try Validate.notEmpty(string: tag)
try Validate.notEmpty(string: key)
let tagName : TagName = TagName.valueOf(tag)
let tagName: TagName = TagName.valueOf(tag)
if(tagNames.contains(tagName) && (enforcedAttributes[tagName] != nil)) {
let attrKey : AttributeKey = AttributeKey.valueOf(key)
var attrMap : Dictionary<AttributeKey, AttributeValue> = enforcedAttributes[tagName]!
let attrKey: AttributeKey = AttributeKey.valueOf(key)
var attrMap: Dictionary<AttributeKey, AttributeValue> = enforcedAttributes[tagName]!
attrMap.removeValue(forKey: attrKey)
enforcedAttributes[tagName] = attrMap
if(attrMap.isEmpty){ // Remove tag from enforced attribute map if no enforced attributes are present
if(attrMap.isEmpty) { // Remove tag from enforced attribute map if no enforced attributes are present
enforcedAttributes.removeValue(forKey: tagName)
}
}
@ -414,7 +400,7 @@ public class Whitelist {
* @see #addProtocols
*/
@discardableResult
open func preserveRelativeLinks(_ preserve: Bool)->Whitelist {
open func preserveRelativeLinks(_ preserve: Bool) -> Whitelist {
preserveRelativeLinks = preserve
return self
}
@ -441,10 +427,10 @@ public class Whitelist {
try Validate.notEmpty(string: key)
try Validate.notNull(obj: protocols)
let tagName : TagName = TagName.valueOf(tag)
let attrKey : AttributeKey = AttributeKey.valueOf(key)
var attrMap : Dictionary<AttributeKey, Set<Protocol>>
var protSet : Set<Protocol>
let tagName: TagName = TagName.valueOf(tag)
let attrKey: AttributeKey = AttributeKey.valueOf(key)
var attrMap: Dictionary<AttributeKey, Set<Protocol>>
var protSet: Set<Protocol>
if (self.protocols[tagName] != nil) {
attrMap = self.protocols[tagName]!
@ -460,10 +446,9 @@ public class Whitelist {
attrMap[attrKey] = protSet
self.protocols[tagName] = attrMap
}
for ptl in protocols
{
for ptl in protocols {
try Validate.notEmpty(string: ptl)
let prot : Protocol = Protocol.valueOf(ptl)
let prot: Protocol = Protocol.valueOf(ptl)
protSet.insert(prot)
}
attrMap[attrKey] = protSet
@ -487,24 +472,23 @@ public class Whitelist {
try Validate.notEmpty(string: tag)
try Validate.notEmpty(string: key)
let tagName : TagName = TagName.valueOf(tag)
let attrKey : AttributeKey = AttributeKey.valueOf(key)
let tagName: TagName = TagName.valueOf(tag)
let attrKey: AttributeKey = AttributeKey.valueOf(key)
if(self.protocols[tagName] != nil) {
var attrMap : Dictionary<AttributeKey, Set<Protocol>>= self.protocols[tagName]!
var attrMap: Dictionary<AttributeKey, Set<Protocol>>= self.protocols[tagName]!
if(attrMap[attrKey] != nil) {
var protSet : Set<Protocol> = attrMap[attrKey]!
for ptl in protocols
{
var protSet: Set<Protocol> = attrMap[attrKey]!
for ptl in protocols {
try Validate.notEmpty(string: ptl)
let prot : Protocol = Protocol.valueOf(ptl)
let prot: Protocol = Protocol.valueOf(ptl)
protSet.remove(prot)
}
attrMap[attrKey] = protSet
if(protSet.isEmpty) { // Remove protocol set if empty
attrMap.removeValue(forKey: attrKey)
if(attrMap.isEmpty){ // Remove entry for tag if empty
if(attrMap.isEmpty) { // Remove entry for tag if empty
self.protocols.removeValue(forKey: tagName)
}
@ -520,7 +504,7 @@ public class Whitelist {
* @param tag test tag
* @return true if allowed
*/
public func isSafeTag(_ tag: String)->Bool {
public func isSafeTag(_ tag: String) -> Bool {
return tagNames.contains(TagName.valueOf(tag))
}
@ -531,9 +515,9 @@ public class Whitelist {
* @param attr attribute under test
* @return true if allowed
*/
public func isSafeAttribute(_ tagName: String, _ el: Element, _ attr: Attribute)->Bool {
let tag : TagName = TagName.valueOf(tagName)
let key : AttributeKey = AttributeKey.valueOf(attr.getKey())
public func isSafeAttribute(_ tagName: String, _ el: Element, _ attr: Attribute) -> Bool {
let tag: TagName = TagName.valueOf(tagName)
let key: AttributeKey = AttributeKey.valueOf(attr.getKey())
if (attributes[tag] != nil) {
if (attributes[tag]?.contains(key))! {
@ -554,16 +538,15 @@ public class Whitelist {
private func testValidProtocol(_ el: Element, _ attr: Attribute, _ protocols: Set<Protocol>)throws->Bool {
// try to resolve relative urls to abs, and optionally update the attribute so output html has abs.
// rels without a baseuri get removed
var value : String = try el.absUrl(attr.getKey())
if (value.characters.count == 0){
var value: String = try el.absUrl(attr.getKey())
if (value.characters.count == 0) {
value = attr.getValue() // if it could not be made abs, run as-is to allow custom unknown protocols
if (!preserveRelativeLinks){
if (!preserveRelativeLinks) {
attr.setValue(value: value)
}
for ptl in protocols
{
var prot : String = ptl.toString()
for ptl in protocols {
var prot: String = ptl.toString()
if (prot=="#") { // allows anchor links
if (isValidAnchor(value)) {
@ -584,18 +567,15 @@ public class Whitelist {
return false
}
private func isValidAnchor(_ value: String)->Bool
{
private func isValidAnchor(_ value: String) -> Bool {
return value.startsWith("#") && !(Pattern(".*\\s.*").matcher(in: value).count > 0)
}
public func getEnforcedAttributes(_ tagName: String)throws->Attributes {
let attrs: Attributes = Attributes()
let tag: TagName = TagName.valueOf(tagName)
if let keyVals: Dictionary<AttributeKey, AttributeValue> = enforcedAttributes[tag]
{
for entry in keyVals
{
if let keyVals: Dictionary<AttributeKey, AttributeValue> = enforcedAttributes[tag] {
for entry in keyVals {
try attrs.put(entry.key.toString(), entry.value.toString())
}
}
@ -604,61 +584,56 @@ public class Whitelist {
}
// named types for config. All just hold strings, but here for my sanity.
open class TagName : TypedValue {
open class TagName: TypedValue {
override init(_ value: String) {
super.init(value)
}
static func valueOf(_ value: String)->TagName{
static func valueOf(_ value: String) -> TagName {
return TagName(value)
}
}
open class AttributeKey : TypedValue {
open class AttributeKey: TypedValue {
override init(_ value: String) {
super.init(value)
}
static func valueOf(_ value: String)->AttributeKey {
static func valueOf(_ value: String) -> AttributeKey {
return AttributeKey(value)
}
}
open class AttributeValue : TypedValue {
open class AttributeValue: TypedValue {
override init(_ value: String) {
super.init(value)
}
static func valueOf(_ value: String)->AttributeValue {
static func valueOf(_ value: String) -> AttributeValue {
return AttributeValue(value)
}
}
open class Protocol : TypedValue {
open class Protocol: TypedValue {
override init(_ value: String) {
super.init(value)
}
static func valueOf(_ value: String)->Protocol {
static func valueOf(_ value: String) -> Protocol {
return Protocol(value)
}
}
open class TypedValue
{
fileprivate let value : String
open class TypedValue {
fileprivate let value: String
init(_ value: String) {
self.value = value
}
public func toString()->String {
public func toString() -> String {
return value
}
}
@ -667,13 +642,12 @@ extension TypedValue: Hashable {
public var hashValue: Int {
let prime = 31
var result = 1
result = Int.addWithOverflow(Int.multiplyWithOverflow(prime,result).0, value.hash).0
result = Int.addWithOverflow(Int.multiplyWithOverflow(prime, result).0, value.hash).0
return result
}
}
public func == (lhs: TypedValue, rhs: TypedValue) -> Bool
{
if(lhs === rhs){return true}
public func == (lhs: TypedValue, rhs: TypedValue) -> Bool {
if(lhs === rhs) {return true}
return lhs.value == rhs.value
}

View File

@ -12,7 +12,7 @@ import Foundation
An XML Declaration.
@author Jonathan Hedley, jonathan@hedley.net */
public class XmlDeclaration : Node {
public class XmlDeclaration: Node {
private let _name: String
private let isProcessingInstruction: Bool // <! if true, <? if false, declaration (and last data char should be ?)
@ -28,16 +28,15 @@ public class XmlDeclaration : Node {
super.init(baseUri)
}
public override func nodeName()->String {
public override func nodeName() -> String {
return "#declaration"
}
/**
* Get the name of this declaration.
* @return name of this declaration.
*/
public func name()->String {
public func name() -> String {
return _name
}
@ -54,9 +53,9 @@ public class XmlDeclaration : Node {
.append("<")
.append(isProcessingInstruction ? "!" : "?")
.append(_name)
do{
do {
try attributes?.html(accum: accum, out: out)
}catch{}
} catch {}
accum
.append(isProcessingInstruction ? "!" : "?")
.append(">")
@ -64,26 +63,23 @@ public class XmlDeclaration : Node {
override func outerHtmlTail(_ accum: StringBuilder, _ depth: Int, _ out: OutputSettings) {}
public override func toString()->String {
do{
public override func toString() -> String {
do {
return try outerHtml()
}catch{}
} catch {}
return ""
}
public override func copy(with zone: NSZone? = nil) -> Any
{
let clone = XmlDeclaration(_name,baseUri!,isProcessingInstruction)
public override func copy(with zone: NSZone? = nil) -> Any {
let clone = XmlDeclaration(_name, baseUri!, isProcessingInstruction)
return copy(clone: clone)
}
public override func copy(parent: Node?)->Node
{
let clone = XmlDeclaration(_name,baseUri!,isProcessingInstruction)
return copy(clone: clone,parent: parent)
public override func copy(parent: Node?) -> Node {
let clone = XmlDeclaration(_name, baseUri!, isProcessingInstruction)
return copy(clone: clone, parent: parent)
}
public override func copy(clone: Node, parent: Node?)->Node
{
return super.copy(clone: clone,parent: parent)
public override func copy(clone: Node, parent: Node?) -> Node {
return super.copy(clone: clone, parent: parent)
}
}

View File

@ -14,18 +14,16 @@ import Foundation
* <p>Usage example: {@code Document xmlDoc = Jsoup.parse(html, baseUrl, Parser.xmlParser())}</p>
*
*/
public class XmlTreeBuilder : TreeBuilder {
public class XmlTreeBuilder: TreeBuilder {
public override init(){
public override init() {
super.init()
}
public override func defaultSettings()->ParseSettings {
public override func defaultSettings() -> ParseSettings {
return ParseSettings.preserveCase
}
public func parse(_ input: String, _ baseUri: String)throws->Document {
return try parse(input, baseUri, ParseErrorList.noTracking(), ParseSettings.preserveCase)
}
@ -36,7 +34,6 @@ public class XmlTreeBuilder : TreeBuilder {
doc.outputSettings().syntax(syntax: OutputSettings.Syntax.xml)
}
override public func process(_ token: Token)throws->Bool {
// start tag, end tag, doctype, comment, character, eof
switch (token.type) {
@ -121,24 +118,21 @@ public class XmlTreeBuilder : TreeBuilder {
let elName: String = try endTag.name()
var firstFound: Element? = nil
for pos in (0..<stack.count).reversed()
{
for pos in (0..<stack.count).reversed() {
let next: Element = stack[pos]
if (next.nodeName().equals(elName)) {
firstFound = next
break
}
}
if (firstFound == nil){
if (firstFound == nil) {
return // not found, skip
}
for pos in (0..<stack.count).reversed()
{
for pos in (0..<stack.count).reversed() {
let next: Element = stack[pos]
stack.remove(at: pos)
if (next == firstFound!){
if (next == firstFound!) {
break
}
}

View File

@ -6,7 +6,6 @@
// Copyright © 2016 Nabil Chatbi. All rights reserved.
//
import XCTest
@testable import SwiftSoupTests

View File

@ -58,13 +58,13 @@ class AttributeParseTest: XCTestCase {
let html: String = "<a id=1 href='?foo=bar&mid&lt=true'>One</a> <a id=2 href='?foo=bar&lt;qux&lg=1'>Two</a>"
let els: Elements = try SwiftSoup.parse(html).select("a")
XCTAssertEqual("?foo=bar&mid&lt=true", try els.first()!.attr("href"))
XCTAssertEqual("?foo=bar<qux&lg=1",try els.last()!.attr("href"))
XCTAssertEqual("?foo=bar<qux&lg=1", try els.last()!.attr("href"))
}
func testmoreAttributeUnescapes()throws {
let html: String = "<a href='&wr_id=123&mid-size=true&ok=&wr'>Check</a>"
let els: Elements = try SwiftSoup.parse(html).select("a")
XCTAssertEqual("&wr_id=123&mid-size=true&ok=&wr",try els.first()!.attr("href"))
XCTAssertEqual("&wr_id=123&mid-size=true&ok=&wr", try els.first()!.attr("href"))
}
func testparsesBooleanAttributes()throws {
@ -76,12 +76,12 @@ class AttributeParseTest: XCTestCase {
XCTAssertEqual("", try el.attr("empty"))
let attributes: Array<Attribute> = el.getAttributes()!.asList()
XCTAssertEqual(3, attributes.count,"There should be 3 attribute present")
XCTAssertEqual(3, attributes.count, "There should be 3 attribute present")
// Assuming the list order always follows the parsed html
XCTAssertFalse((attributes[0] as? BooleanAttribute) != nil,"'normal' attribute should not be boolean")
XCTAssertTrue((attributes[1] as? BooleanAttribute) != nil,"'boolean' attribute should be boolean")
XCTAssertFalse((attributes[2] as? BooleanAttribute) != nil,"'empty' attribute should not be boolean")
XCTAssertFalse((attributes[0] as? BooleanAttribute) != nil, "'normal' attribute should not be boolean")
XCTAssertTrue((attributes[1] as? BooleanAttribute) != nil, "'boolean' attribute should be boolean")
XCTAssertFalse((attributes[2] as? BooleanAttribute) != nil, "'empty' attribute should not be boolean")
XCTAssertEqual(html, try el.outerHtml())
}
@ -89,7 +89,7 @@ class AttributeParseTest: XCTestCase {
func testdropsSlashFromAttributeName()throws {
let html: String = "<img /onerror='doMyJob'/>"
var doc: Document = try SwiftSoup.parse(html)
XCTAssertTrue(try doc.select("img[onerror]").size() != 0,"SelfClosingStartTag ignores last character")
XCTAssertTrue(try doc.select("img[onerror]").size() != 0, "SelfClosingStartTag ignores last character")
XCTAssertEqual("<img onerror=\"doMyJob\">", try doc.body()!.html())
doc = try SwiftSoup.parse(html, "", Parser.xmlParser())
@ -98,14 +98,14 @@ class AttributeParseTest: XCTestCase {
static var allTests = {
return [
("testparsesRoughAttributeString" , testparsesRoughAttributeString),
("testhandlesNewLinesAndReturns" , testhandlesNewLinesAndReturns),
("testparsesEmptyString" , testparsesEmptyString),
("testcanStartWithEq" , testcanStartWithEq),
("teststrictAttributeUnescapes" , teststrictAttributeUnescapes),
("testmoreAttributeUnescapes" , testmoreAttributeUnescapes),
("testparsesBooleanAttributes" , testparsesBooleanAttributes),
("testdropsSlashFromAttributeName" , testdropsSlashFromAttributeName),
("testparsesRoughAttributeString", testparsesRoughAttributeString),
("testhandlesNewLinesAndReturns", testhandlesNewLinesAndReturns),
("testparsesEmptyString", testparsesEmptyString),
("testcanStartWithEq", testcanStartWithEq),
("teststrictAttributeUnescapes", teststrictAttributeUnescapes),
("testmoreAttributeUnescapes", testmoreAttributeUnescapes),
("testparsesBooleanAttributes", testparsesBooleanAttributes),
("testdropsSlashFromAttributeName", testdropsSlashFromAttributeName),
]
}()

View File

@ -10,8 +10,7 @@ import XCTest
@testable import SwiftSoup
class AttributeTest: XCTestCase {
func testHtml()
{
func testHtml() {
let attr = try! Attribute(key: "key", value: "value &")
XCTAssertEqual("key=\"value &amp;\"", attr.html())
XCTAssertEqual(attr.html(), attr.toString())
@ -26,8 +25,8 @@ class AttributeTest: XCTestCase {
static var allTests = {
return [
("testHtml" , testHtml),
("testWithSupplementaryCharacterInAttributeKeyAndValue" , testWithSupplementaryCharacterInAttributeKeyAndValue)
("testHtml", testHtml),
("testWithSupplementaryCharacterInAttributeKeyAndValue", testWithSupplementaryCharacterInAttributeKeyAndValue)
]
}()

View File

@ -13,12 +13,11 @@ class AttributesTest: XCTestCase {
func testHtml() {
let a: Attributes = Attributes()
do{
do {
try a.put("Tot", "a&p")
try a.put("Hello", "There")
try a.put("data-name", "Jsoup")
}catch{}
} catch {}
XCTAssertEqual(3, a.size())
XCTAssertTrue(a.hasKey(key: "Tot"))
@ -26,7 +25,7 @@ class AttributesTest: XCTestCase {
XCTAssertTrue(a.hasKey(key: "data-name"))
XCTAssertFalse(a.hasKey(key: "tot"))
XCTAssertTrue(a.hasKeyIgnoreCase(key: "tot"))
XCTAssertEqual("There",try a.getIgnoreCase(key: "hEllo"))
XCTAssertEqual("There", try a.getIgnoreCase(key: "hEllo"))
XCTAssertEqual(1, a.dataset().count)
XCTAssertEqual("Jsoup", a.dataset()["name"])
@ -53,10 +52,9 @@ class AttributesTest: XCTestCase {
// XCTAssertEqual(2, a.size())
// }
func testIterator() {
let a: Attributes = Attributes()
let datas: [[String]] = [["Tot", "raul"],["Hello", "pismuth"],["data-name", "Jsoup"]]
let datas: [[String]] = [["Tot", "raul"], ["Hello", "pismuth"], ["data-name", "Jsoup"]]
for atts in datas {
try! a.put(atts[0], atts[1])
@ -82,9 +80,9 @@ class AttributesTest: XCTestCase {
static var allTests = {
return [
("testHtml" , testHtml),
("testIterator" , testIterator),
("testIteratorEmpty" , testIteratorEmpty)
("testHtml", testHtml),
("testIterator", testIterator),
("testIteratorEmpty", testIteratorEmpty)
]
}()
}

View File

@ -167,7 +167,6 @@ class CharacterReaderTest: XCTestCase {
XCTAssertFalse(r.matches("ne"))
}
func testMatchesIgnoreCase() {
let r = CharacterReader("One Two Three")
XCTAssertTrue(r.matchesIgnoreCase("O"))
@ -245,25 +244,25 @@ class CharacterReaderTest: XCTestCase {
static var allTests = {
return [
("testConsume" , testConsume),
("testUnconsume" , testUnconsume),
("testMark" , testMark),
("testConsumeToEnd" , testConsumeToEnd),
("testNextIndexOfChar" , testNextIndexOfChar),
("testNextIndexOfString" , testNextIndexOfString),
("testNextIndexOfUnmatched" , testNextIndexOfUnmatched),
("testConsumeToChar" , testConsumeToChar),
("testConsumeToString" , testConsumeToString),
("testAdvance" , testAdvance),
("testConsumeToAny" , testConsumeToAny),
("testConsumeLetterSequence" , testConsumeLetterSequence),
("testConsumeLetterThenDigitSequence" , testConsumeLetterThenDigitSequence),
("testMatches" , testMatches),
("testMatchesIgnoreCase" , testMatchesIgnoreCase),
("testContainsIgnoreCase" , testContainsIgnoreCase),
("testMatchesAny" , testMatchesAny),
("testCachesStrings" , testCachesStrings),
("testRangeEquals" , testRangeEquals)
("testConsume", testConsume),
("testUnconsume", testUnconsume),
("testMark", testMark),
("testConsumeToEnd", testConsumeToEnd),
("testNextIndexOfChar", testNextIndexOfChar),
("testNextIndexOfString", testNextIndexOfString),
("testNextIndexOfUnmatched", testNextIndexOfUnmatched),
("testConsumeToChar", testConsumeToChar),
("testConsumeToString", testConsumeToString),
("testAdvance", testAdvance),
("testConsumeToAny", testConsumeToAny),
("testConsumeLetterSequence", testConsumeLetterSequence),
("testConsumeLetterThenDigitSequence", testConsumeLetterThenDigitSequence),
("testMatches", testMatches),
("testMatchesIgnoreCase", testMatchesIgnoreCase),
("testContainsIgnoreCase", testContainsIgnoreCase),
("testMatchesAny", testMatchesAny),
("testCachesStrings", testCachesStrings),
("testRangeEquals", testRangeEquals)
]
}()

View File

@ -19,13 +19,13 @@ class CssTest: XCTestCase {
let sb: StringBuilder = StringBuilder(string:"<html><head></head><body>")
sb.append("<div id='pseudo'>")
for i in 1...10{
for i in 1...10 {
sb.append("<p>\(i)</p>")
}
sb.append("</div>")
sb.append("<div id='type'>")
for i in 1...10{
for i in 1...10 {
sb.append("<p>\(i)</p>")
sb.append("<span>\(i)</span>")
sb.append("<em>\(i)</em>")
@ -45,7 +45,6 @@ class CssTest: XCTestCase {
html = try! SwiftSoup.parse(htmlString)
}
func testFirstChild()throws {
try check(html.select("#pseudo :first-child"), "1")
try check(html.select("html:first-child"))
@ -82,7 +81,7 @@ class CssTest: XCTestCase {
func testNthLastOfType_simple()throws {
for i in 1...10 {
try check(html.select("#type :nth-last-of-type(\(i))"), "\(11-i)","\(11-i)","\(11-i)","\(11-i)")
try check(html.select("#type :nth-last-of-type(\(i))"), "\(11-i)", "\(11-i)", "\(11-i)", "\(11-i)")
}
}
@ -112,7 +111,6 @@ class CssTest: XCTestCase {
try check(html.select("#type :nth-of-type(+5)"), "5", "5", "5", "5")
}
func testNthLastChild_advanced()throws {
try check(html.select("#pseudo :nth-last-child(-5)"))
try check(html.select("#pseudo :nth-last-child(odd)"), "2", "4", "6", "8", "10")
@ -177,9 +175,8 @@ class CssTest: XCTestCase {
XCTAssertEqual("em", sel.get(5).tagName())
}
func check(_ resut: Elements, _ expectedContent: String... ) {
check(resut,expectedContent)
check(resut, expectedContent)
}
func check(_ result: Elements, _ expectedContent: [String] ) {
@ -202,28 +199,25 @@ class CssTest: XCTestCase {
try XCTAssertEqual(Tag.valueOf("body"), sel2.get(0).tag())
}
static var allTests = {
return [
("testFirstChild" , testFirstChild),
("testLastChild" , testLastChild),
("testNthChild_simple" , testNthChild_simple),
("testNthOfType_unknownTag" , testNthOfType_unknownTag),
("testNthLastChild_simple" , testNthLastChild_simple),
("testNthOfType_simple" , testNthOfType_simple),
("testNthLastOfType_simple" , testNthLastOfType_simple),
("testNthChild_advanced" , testNthChild_advanced),
("testNthOfType_advanced" , testNthOfType_advanced),
("testNthLastChild_advanced" , testNthLastChild_advanced),
("testNthLastOfType_advanced" , testNthLastOfType_advanced),
("testFirstOfType" , testFirstOfType),
("testLastOfType" , testLastOfType),
("testEmpty" , testEmpty),
("testOnlyChild" , testOnlyChild),
("testOnlyOfType" , testOnlyOfType),
("testRoot" , testRoot)
("testFirstChild", testFirstChild),
("testLastChild", testLastChild),
("testNthChild_simple", testNthChild_simple),
("testNthOfType_unknownTag", testNthOfType_unknownTag),
("testNthLastChild_simple", testNthLastChild_simple),
("testNthOfType_simple", testNthOfType_simple),
("testNthLastOfType_simple", testNthLastOfType_simple),
("testNthChild_advanced", testNthChild_advanced),
("testNthOfType_advanced", testNthOfType_advanced),
("testNthLastChild_advanced", testNthLastChild_advanced),
("testNthLastOfType_advanced", testNthLastOfType_advanced),
("testFirstOfType", testFirstOfType),
("testLastOfType", testLastOfType),
("testEmpty", testEmpty),
("testOnlyChild", testOnlyChild),
("testOnlyOfType", testOnlyOfType),
("testRoot", testRoot)
]
}()
}

View File

@ -15,19 +15,19 @@ class DocumentTest: XCTestCase {
private static let charsetIso8859 = String.Encoding.iso2022JP //"ISO-8859-1"
func testSetTextPreservesDocumentStructure() {
do{
do {
let doc: Document = try SwiftSoup.parse("<p>Hello</p>")
try doc.text("Replaced")
XCTAssertEqual("Replaced", try doc.text())
XCTAssertEqual("Replaced", try doc.body()!.text())
XCTAssertEqual(1, try doc.select("head").size())
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
func testTitles() {
do{
do {
let noTitle: Document = try SwiftSoup.parse("<p>Hello</p>")
let withTitle: Document = try SwiftSoup.parse("<title>First</title><title>Ignore</title><p>Hello</p>")
@ -43,35 +43,34 @@ class DocumentTest: XCTestCase {
let normaliseTitle: Document = try SwiftSoup.parse("<title> Hello\nthere \n now \n")
XCTAssertEqual("Hello there now", try normaliseTitle.title())
}catch{
} catch {
}
}
func testOutputEncoding() {
do{
do {
let doc: Document = try SwiftSoup.parse("<p title=π>π & < > </p>")
// default is utf-8
XCTAssertEqual("<p title=\"π\">π &amp; &lt; &gt; </p>", try doc.body()?.html())
XCTAssertEqual("UTF-8",doc.outputSettings().charset().displayName())
XCTAssertEqual("UTF-8", doc.outputSettings().charset().displayName())
doc.outputSettings().charset(String.Encoding.ascii)
XCTAssertEqual(Entities.EscapeMode.base,doc.outputSettings().escapeMode())
XCTAssertEqual(Entities.EscapeMode.base, doc.outputSettings().escapeMode())
XCTAssertEqual("<p title=\"&#x3c0;\">&#x3c0; &amp; &lt; &gt; </p>", try doc.body()?.html())
doc.outputSettings().escapeMode(Entities.EscapeMode.extended)
XCTAssertEqual("<p title=\"&pi;\">&pi; &amp; &lt; &gt; </p>", try doc.body()?.html())
}catch
{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
func testXhtmlReferences() {
let doc: Document = try! SwiftSoup.parse("&lt; &gt; &amp; &quot; &apos; &times;")
doc.outputSettings().escapeMode(Entities.EscapeMode.xhtml)
XCTAssertEqual("&lt; &gt; &amp; \" ' ×",try! doc.body()?.html())
XCTAssertEqual("&lt; &gt; &amp; \" ' ×", try! doc.body()?.html())
}
func testNormalisesStructure() {
@ -83,7 +82,7 @@ class DocumentTest: XCTestCase {
let doc: Document = try! SwiftSoup.parse("<title>Hello</title> <p>One<p>Two")
let clone: Document = doc.copy() as! Document
XCTAssertEqual("<html><head><title>Hello</title> </head><body><p>One</p><p>Two</p></body></html>",try! TextUtil.stripNewlines(clone.html()))
XCTAssertEqual("<html><head><title>Hello</title> </head><body><p>One</p><p>Two</p></body></html>", try! TextUtil.stripNewlines(clone.html()))
try! clone.title("Hello there")
try! clone.select("p").first()!.text("One more").attr("id", "1")
XCTAssertEqual("<html><head><title>Hello there</title> </head><body><p id=\"1\">One more</p><p>Two</p></body></html>", try! TextUtil.stripNewlines(clone.html()))
@ -191,13 +190,12 @@ class DocumentTest: XCTestCase {
func testMetaCharsetUpdateUtf8() {
let doc: Document = createHtmlDocument("changeThis")
doc.updateMetaCharsetElement(true)
do{
do {
try doc.charset(DocumentTest.charsetUtf8)
}catch{
} catch {
print("")
}
let htmlCharsetUTF8: String = "<html>\n" + " <head>\n" + " <meta charset=\"" + "UTF-8" + "\">\n" + " </head>\n" + " <body></body>\n" + "</html>"
XCTAssertEqual(htmlCharsetUTF8, try! doc.toString())
@ -219,7 +217,7 @@ class DocumentTest: XCTestCase {
" </head>\n" +
" <body></body>\n" +
"</html>"
XCTAssertEqual(htmlCharsetISO,try doc.toString())
XCTAssertEqual(htmlCharsetISO, try doc.toString())
let selectedElement: Element = try doc.select("meta[charset]").first()!
XCTAssertEqual(String.Encoding.isoLatin2.displayName(), doc.charset().displayName())
@ -376,8 +374,7 @@ class DocumentTest: XCTestCase {
XCTAssertFalse(doc.updateMetaCharsetElement())
}
private func createHtmlDocument(_ charset: String)->Document {
private func createHtmlDocument(_ charset: String) -> Document {
let doc: Document = Document.createShell("")
try! doc.head()?.appendElement("meta").attr("charset", charset)
try! doc.head()?.appendElement("meta").attr("name", "charset").attr("content", charset)
@ -423,31 +420,31 @@ class DocumentTest: XCTestCase {
static var allTests = {
return [
("testSetTextPreservesDocumentStructure" , testSetTextPreservesDocumentStructure),
("testTitles" , testTitles),
("testOutputEncoding" , testOutputEncoding),
("testXhtmlReferences" , testXhtmlReferences),
("testNormalisesStructure" , testNormalisesStructure),
("testClone" , testClone),
("testClonesDeclarations" , testClonesDeclarations),
("testHtmlAndXmlSyntax" , testHtmlAndXmlSyntax),
("testHtmlParseDefaultsToHtmlOutputSyntax" , testHtmlParseDefaultsToHtmlOutputSyntax),
("testHtmlAppendable" , testHtmlAppendable),
("testDocumentsWithSameContentAreEqual" , testDocumentsWithSameContentAreEqual),
("testDocumentsWithSameContentAreVerifialbe" , testDocumentsWithSameContentAreVerifialbe),
("testMetaCharsetUpdateUtf8" , testMetaCharsetUpdateUtf8),
("testMetaCharsetUpdateIsoLatin2" , testMetaCharsetUpdateIsoLatin2),
("testMetaCharsetUpdateNoCharset" , testMetaCharsetUpdateNoCharset),
("testMetaCharsetUpdateDisabled" , testMetaCharsetUpdateDisabled),
("testMetaCharsetUpdateDisabledNoChanges" , testMetaCharsetUpdateDisabledNoChanges),
("testMetaCharsetUpdateEnabledAfterCharsetChange" , testMetaCharsetUpdateEnabledAfterCharsetChange),
("testMetaCharsetUpdateCleanup" , testMetaCharsetUpdateCleanup),
("testMetaCharsetUpdateXmlUtf8" , testMetaCharsetUpdateXmlUtf8),
("testMetaCharsetUpdateXmlIso2022JP" , testMetaCharsetUpdateXmlIso2022JP),
("testMetaCharsetUpdateXmlNoCharset" , testMetaCharsetUpdateXmlNoCharset),
("testMetaCharsetUpdateXmlDisabled" , testMetaCharsetUpdateXmlDisabled),
("testMetaCharsetUpdateXmlDisabledNoChanges" , testMetaCharsetUpdateXmlDisabledNoChanges),
("testMetaCharsetUpdatedDisabledPerDefault" , testMetaCharsetUpdatedDisabledPerDefault)
("testSetTextPreservesDocumentStructure", testSetTextPreservesDocumentStructure),
("testTitles", testTitles),
("testOutputEncoding", testOutputEncoding),
("testXhtmlReferences", testXhtmlReferences),
("testNormalisesStructure", testNormalisesStructure),
("testClone", testClone),
("testClonesDeclarations", testClonesDeclarations),
("testHtmlAndXmlSyntax", testHtmlAndXmlSyntax),
("testHtmlParseDefaultsToHtmlOutputSyntax", testHtmlParseDefaultsToHtmlOutputSyntax),
("testHtmlAppendable", testHtmlAppendable),
("testDocumentsWithSameContentAreEqual", testDocumentsWithSameContentAreEqual),
("testDocumentsWithSameContentAreVerifialbe", testDocumentsWithSameContentAreVerifialbe),
("testMetaCharsetUpdateUtf8", testMetaCharsetUpdateUtf8),
("testMetaCharsetUpdateIsoLatin2", testMetaCharsetUpdateIsoLatin2),
("testMetaCharsetUpdateNoCharset", testMetaCharsetUpdateNoCharset),
("testMetaCharsetUpdateDisabled", testMetaCharsetUpdateDisabled),
("testMetaCharsetUpdateDisabledNoChanges", testMetaCharsetUpdateDisabledNoChanges),
("testMetaCharsetUpdateEnabledAfterCharsetChange", testMetaCharsetUpdateEnabledAfterCharsetChange),
("testMetaCharsetUpdateCleanup", testMetaCharsetUpdateCleanup),
("testMetaCharsetUpdateXmlUtf8", testMetaCharsetUpdateXmlUtf8),
("testMetaCharsetUpdateXmlIso2022JP", testMetaCharsetUpdateXmlIso2022JP),
("testMetaCharsetUpdateXmlNoCharset", testMetaCharsetUpdateXmlNoCharset),
("testMetaCharsetUpdateXmlDisabled", testMetaCharsetUpdateXmlDisabled),
("testMetaCharsetUpdateXmlDisabledNoChanges", testMetaCharsetUpdateXmlDisabledNoChanges),
("testMetaCharsetUpdatedDisabledPerDefault", testMetaCharsetUpdatedDisabledPerDefault)
]
}()

View File

@ -12,11 +12,10 @@ import SwiftSoup
class DocumentTypeTest: XCTestCase {
func testConstructorValidationOkWithBlankName() {
let fail: DocumentType? = DocumentType("","", "", "")
let fail: DocumentType? = DocumentType("", "", "", "")
XCTAssertTrue(fail != nil)
}
func testConstructorValidationThrowsExceptionOnNulls() {
let fail: DocumentType? = DocumentType("html", "", "", "")
XCTAssertTrue(fail != nil)
@ -43,10 +42,10 @@ class DocumentTypeTest: XCTestCase {
static var allTests = {
return [
("testConstructorValidationOkWithBlankName" , testConstructorValidationOkWithBlankName),
("testConstructorValidationThrowsExceptionOnNulls" , testConstructorValidationThrowsExceptionOnNulls),
("testConstructorValidationOkWithBlankPublicAndSystemIds" , testConstructorValidationOkWithBlankPublicAndSystemIds),
("testOuterHtmlGeneration" , testOuterHtmlGeneration),
("testConstructorValidationOkWithBlankName", testConstructorValidationOkWithBlankName),
("testConstructorValidationThrowsExceptionOnNulls", testConstructorValidationThrowsExceptionOnNulls),
("testConstructorValidationOkWithBlankPublicAndSystemIds", testConstructorValidationOkWithBlankPublicAndSystemIds),
("testOuterHtmlGeneration", testOuterHtmlGeneration),
]
}()
}

View File

@ -354,8 +354,7 @@ class ElementTest: XCTestCase {
// check sibling index (with short circuit on reindexChildren):
let ps: Elements = try doc.select("p")
for i in 0..<ps.size()
{
for i in 0..<ps.size() {
XCTAssertEqual(i, ps.get(i).siblingIndex)
}
}
@ -414,7 +413,7 @@ class ElementTest: XCTestCase {
let doc: Document = try SwiftSoup.parse("<div id=1><p>Hello</p></div>")
let div: Element = try doc.getElementById("1")!
try div.appendText(" there & now >")
XCTAssertEqual("<p>Hello</p> there &amp; now &gt;",try TextUtil.stripNewlines(div.html()))
XCTAssertEqual("<p>Hello</p> there &amp; now &gt;", try TextUtil.stripNewlines(div.html()))
}
func testPrependText()throws {
@ -447,7 +446,7 @@ class ElementTest: XCTestCase {
// check sibling index (no reindexChildren):
let ps: Elements = try doc.select("p")
for i in 0..<ps.size(){
for i in 0..<ps.size() {
XCTAssertEqual(i, ps.get(i).siblingIndex)
}
}
@ -460,7 +459,7 @@ class ElementTest: XCTestCase {
// check sibling index (reindexChildren):
let ps: Elements = try doc.select("p")
for i in 0..<ps.size(){
for i in 0..<ps.size() {
XCTAssertEqual(i, ps.get(i).siblingIndex)
}
}
@ -516,7 +515,7 @@ class ElementTest: XCTestCase {
XCTAssertEqual("<div><p>Hello</p><div>one</div><div>two</div><p>There</p></div>", try TextUtil.stripNewlines(doc.body()!.html()))
try doc.select("p").last()?.after("<p>Three</p><!-- four -->")
XCTAssertEqual("<div><p>Hello</p><div>one</div><div>two</div><p>There</p><p>Three</p><!-- four --></div>",TextUtil.stripNewlines(try doc.body()!.html()))
XCTAssertEqual("<div><p>Hello</p><div>one</div><div>two</div><p>There</p><p>Three</p><!-- four --></div>", TextUtil.stripNewlines(try doc.body()!.html()))
}
func testWrapWithRemainder()throws {
@ -592,12 +591,12 @@ class ElementTest: XCTestCase {
XCTAssertNotNil(p.parent())
try clone.append("<span>Three")
XCTAssertEqual("<p><span>Two</span><span>Three</span></p>",try TextUtil.stripNewlines(clone.outerHtml()))
XCTAssertEqual("<div><p>One</p><p><span>Two</span></p></div>",try TextUtil.stripNewlines(doc.body()!.html())) // not modified
XCTAssertEqual("<p><span>Two</span><span>Three</span></p>", try TextUtil.stripNewlines(clone.outerHtml()))
XCTAssertEqual("<div><p>One</p><p><span>Two</span></p></div>", try TextUtil.stripNewlines(doc.body()!.html())) // not modified
try doc.body()?.appendChild(clone) // adopt
XCTAssertNotNil(clone.parent())
XCTAssertEqual("<div><p>One</p><p><span>Two</span></p></div><p><span>Two</span><span>Three</span></p>",try TextUtil.stripNewlines(doc.body()!.html()))
XCTAssertEqual("<div><p>One</p><p><span>Two</span></p></div><p><span>Two</span><span>Three</span></p>", try TextUtil.stripNewlines(doc.body()!.html()))
}
func testClonesClassnames()throws {
@ -718,7 +717,7 @@ class ElementTest: XCTestCase {
XCTAssertEqual(0, children.count) // children is backed by div1.childNodes, moved, so should be 0 now
XCTAssertEqual(0, div1.childNodeSize())
XCTAssertEqual(4, div2.childNodeSize())
XCTAssertEqual("<div id=\"1\"></div>\n<div id=\"2\">\n Text \n <p>One</p> Text \n <p>Two</p>\n</div>",try doc.body()!.html())
XCTAssertEqual("<div id=\"1\"></div>\n<div id=\"2\">\n Text \n <p>One</p> Text \n <p>Two</p>\n</div>", try doc.body()!.html())
}
func testInsertChildrenArgumentValidation()throws {
@ -729,13 +728,13 @@ class ElementTest: XCTestCase {
do {
try div2.insertChildren(6, children)
XCTAssertEqual(0,1)
} catch{}
XCTAssertEqual(0, 1)
} catch {}
do {
try div2.insertChildren(-5, children)
XCTAssertEqual(0,1)
} catch{
XCTAssertEqual(0, 1)
} catch {
}
}
@ -820,7 +819,6 @@ class ElementTest: XCTestCase {
try div.classNames(newSet)
XCTAssertEqual("c1 c2 c3", try div.className())
let set2 = try div.classNames()
@ -932,75 +930,75 @@ class ElementTest: XCTestCase {
static var allTests = {
return [
("testGetElementsByTagName" , testGetElementsByTagName),
("testGetNamespacedElementsByTag" , testGetNamespacedElementsByTag),
("testGetElementById" , testGetElementById),
("testGetText" , testGetText),
("testGetChildText" , testGetChildText),
("testNormalisesText" , testNormalisesText),
("testKeepsPreText" , testKeepsPreText),
("testKeepsPreTextInCode" , testKeepsPreTextInCode),
("testBrHasSpace" , testBrHasSpace),
("testGetSiblings" , testGetSiblings),
("testGetSiblingsWithDuplicateContent" , testGetSiblingsWithDuplicateContent),
("testGetParents" , testGetParents),
("testElementSiblingIndex" , testElementSiblingIndex),
("testElementSiblingIndexSameContent" , testElementSiblingIndexSameContent),
("testGetElementsWithClass" , testGetElementsWithClass),
("testGetElementsWithAttribute" , testGetElementsWithAttribute),
("testGetElementsWithAttributeDash" , testGetElementsWithAttributeDash),
("testGetElementsWithAttributeValue" , testGetElementsWithAttributeValue),
("testClassDomMethods" , testClassDomMethods),
("testHasClassDomMethods" , testHasClassDomMethods),
("testClassUpdates" , testClassUpdates),
("testOuterHtml" , testOuterHtml),
("testInnerHtml" , testInnerHtml),
("testFormatHtml" , testFormatHtml),
("testFormatOutline" , testFormatOutline),
("testSetIndent" , testSetIndent),
("testNotPretty" , testNotPretty),
("testEmptyElementFormatHtml" , testEmptyElementFormatHtml),
("testNoIndentOnScriptAndStyle" , testNoIndentOnScriptAndStyle),
("testContainerOutput" , testContainerOutput),
("testSetText" , testSetText),
("testAddNewElement" , testAddNewElement),
("testAddBooleanAttribute" , testAddBooleanAttribute),
("testAppendRowToTable" , testAppendRowToTable),
("testPrependRowToTable" , testPrependRowToTable),
("testPrependElement" , testPrependElement),
("testAddNewText" , testAddNewText),
("testPrependText" , testPrependText),
("testAddNewHtml" , testAddNewHtml),
("testPrependNewHtml" , testPrependNewHtml),
("testSetHtml" , testSetHtml),
("testSetHtmlTitle" , testSetHtmlTitle),
("testWrap" , testWrap),
("testBefore" , testBefore),
("testAfter" , testAfter),
("testWrapWithRemainder" , testWrapWithRemainder),
("testHasText" , testHasText),
("testDataset" , testDataset),
("testpParentlessToString" , testpParentlessToString),
("testClone" , testClone),
("testClonesClassnames" , testClonesClassnames),
("testTagNameSet" , testTagNameSet),
("testHtmlContainsOuter" , testHtmlContainsOuter),
("testGetTextNodes" , testGetTextNodes),
("testManipulateTextNodes" , testManipulateTextNodes),
("testGetDataNodes" , testGetDataNodes),
("testElementIsNotASiblingOfItself" , testElementIsNotASiblingOfItself),
("testChildThrowsIndexOutOfBoundsOnMissing" , testChildThrowsIndexOutOfBoundsOnMissing),
("testMoveByAppend" , testMoveByAppend),
("testInsertChildrenArgumentValidation" , testInsertChildrenArgumentValidation),
("testInsertChildrenAtPosition" , testInsertChildrenAtPosition),
("testInsertChildrenAsCopy" , testInsertChildrenAsCopy),
("testCssPath" , testCssPath),
("testClassNames" , testClassNames),
("testHashAndEqualsAndValue" , testHashAndEqualsAndValue),
("testRelativeUrls" , testRelativeUrls),
("testAppendMustCorrectlyMoveChildrenInsideOneParentElement" , testAppendMustCorrectlyMoveChildrenInsideOneParentElement),
("testHashcodeIsStableWithContentChanges" , testHashcodeIsStableWithContentChanges),
("testNamespacedElements" , testNamespacedElements)
("testGetElementsByTagName", testGetElementsByTagName),
("testGetNamespacedElementsByTag", testGetNamespacedElementsByTag),
("testGetElementById", testGetElementById),
("testGetText", testGetText),
("testGetChildText", testGetChildText),
("testNormalisesText", testNormalisesText),
("testKeepsPreText", testKeepsPreText),
("testKeepsPreTextInCode", testKeepsPreTextInCode),
("testBrHasSpace", testBrHasSpace),
("testGetSiblings", testGetSiblings),
("testGetSiblingsWithDuplicateContent", testGetSiblingsWithDuplicateContent),
("testGetParents", testGetParents),
("testElementSiblingIndex", testElementSiblingIndex),
("testElementSiblingIndexSameContent", testElementSiblingIndexSameContent),
("testGetElementsWithClass", testGetElementsWithClass),
("testGetElementsWithAttribute", testGetElementsWithAttribute),
("testGetElementsWithAttributeDash", testGetElementsWithAttributeDash),
("testGetElementsWithAttributeValue", testGetElementsWithAttributeValue),
("testClassDomMethods", testClassDomMethods),
("testHasClassDomMethods", testHasClassDomMethods),
("testClassUpdates", testClassUpdates),
("testOuterHtml", testOuterHtml),
("testInnerHtml", testInnerHtml),
("testFormatHtml", testFormatHtml),
("testFormatOutline", testFormatOutline),
("testSetIndent", testSetIndent),
("testNotPretty", testNotPretty),
("testEmptyElementFormatHtml", testEmptyElementFormatHtml),
("testNoIndentOnScriptAndStyle", testNoIndentOnScriptAndStyle),
("testContainerOutput", testContainerOutput),
("testSetText", testSetText),
("testAddNewElement", testAddNewElement),
("testAddBooleanAttribute", testAddBooleanAttribute),
("testAppendRowToTable", testAppendRowToTable),
("testPrependRowToTable", testPrependRowToTable),
("testPrependElement", testPrependElement),
("testAddNewText", testAddNewText),
("testPrependText", testPrependText),
("testAddNewHtml", testAddNewHtml),
("testPrependNewHtml", testPrependNewHtml),
("testSetHtml", testSetHtml),
("testSetHtmlTitle", testSetHtmlTitle),
("testWrap", testWrap),
("testBefore", testBefore),
("testAfter", testAfter),
("testWrapWithRemainder", testWrapWithRemainder),
("testHasText", testHasText),
("testDataset", testDataset),
("testpParentlessToString", testpParentlessToString),
("testClone", testClone),
("testClonesClassnames", testClonesClassnames),
("testTagNameSet", testTagNameSet),
("testHtmlContainsOuter", testHtmlContainsOuter),
("testGetTextNodes", testGetTextNodes),
("testManipulateTextNodes", testManipulateTextNodes),
("testGetDataNodes", testGetDataNodes),
("testElementIsNotASiblingOfItself", testElementIsNotASiblingOfItself),
("testChildThrowsIndexOutOfBoundsOnMissing", testChildThrowsIndexOutOfBoundsOnMissing),
("testMoveByAppend", testMoveByAppend),
("testInsertChildrenArgumentValidation", testInsertChildrenArgumentValidation),
("testInsertChildrenAtPosition", testInsertChildrenAtPosition),
("testInsertChildrenAsCopy", testInsertChildrenAsCopy),
("testCssPath", testCssPath),
("testClassNames", testClassNames),
("testHashAndEqualsAndValue", testHashAndEqualsAndValue),
("testRelativeUrls", testRelativeUrls),
("testAppendMustCorrectlyMoveChildrenInsideOneParentElement", testAppendMustCorrectlyMoveChildrenInsideOneParentElement),
("testHashcodeIsStableWithContentChanges", testHashcodeIsStableWithContentChanges),
("testNamespacedElements", testNamespacedElements)
]
}()
}

View File

@ -160,7 +160,7 @@ class ElementsTest: XCTestCase {
let h = "<p><b>This</b> is <b>jsoup</b>.</p> <p>How do you like it?</p>"
let doc: Document = try SwiftSoup.parse(h)
try doc.select("p").wrap("<div></div>")
XCTAssertEqual("<div><p><b>This</b> is <b>jsoup</b>.</p></div> <div><p>How do you like it?</p></div>",try TextUtil.stripNewlines(doc.body()!.html()))
XCTAssertEqual("<div><p><b>This</b> is <b>jsoup</b>.</p></div> <div><p>How do you like it?</p></div>", try TextUtil.stripNewlines(doc.body()!.html()))
}
func testUnwrap()throws {
@ -248,7 +248,7 @@ class ElementsTest: XCTestCase {
let doc: Document = try SwiftSoup.parse("<div><p>Hello</p></div><div>There</div>")
let accum: StringBuilder = StringBuilder()
class nv : NodeVisitor {
class nv: NodeVisitor {
let accum: StringBuilder
init(_ accum: StringBuilder) {
self.accum = accum
@ -284,39 +284,38 @@ class ElementsTest: XCTestCase {
try XCTAssertEqual("Check", els.text())
}
static var allTests = {
return [
("testFilter" , testFilter),
("testAttributes" , testAttributes),
("testHasAttr" , testHasAttr),
("testHasAbsAttr" , testHasAbsAttr),
("testAttr" , testAttr),
("testAbsAttr" , testAbsAttr),
("testClasses" , testClasses),
("testText" , testText),
("testHasText" , testHasText),
("testHtml" , testHtml),
("testOuterHtml" , testOuterHtml),
("testSetHtml" , testSetHtml),
("testVal" , testVal),
("testBefore" , testBefore),
("testAfter" , testAfter),
("testWrap" , testWrap),
("testWrapDiv" , testWrapDiv),
("testUnwrap" , testUnwrap),
("testUnwrapP" , testUnwrapP),
("testUnwrapKeepsSpace" , testUnwrapKeepsSpace),
("testEmpty" , testEmpty),
("testRemove" , testRemove),
("testEq" , testEq),
("testIs" , testIs),
("testParents" , testParents),
("testNot" , testNot),
("testTagNameSet" , testTagNameSet),
("testTraverse" , testTraverse),
("testForms" , testForms),
("testClassWithHyphen" , testClassWithHyphen)
("testFilter", testFilter),
("testAttributes", testAttributes),
("testHasAttr", testHasAttr),
("testHasAbsAttr", testHasAbsAttr),
("testAttr", testAttr),
("testAbsAttr", testAbsAttr),
("testClasses", testClasses),
("testText", testText),
("testHasText", testHasText),
("testHtml", testHtml),
("testOuterHtml", testOuterHtml),
("testSetHtml", testSetHtml),
("testVal", testVal),
("testBefore", testBefore),
("testAfter", testAfter),
("testWrap", testWrap),
("testWrapDiv", testWrapDiv),
("testUnwrap", testUnwrap),
("testUnwrapP", testUnwrapP),
("testUnwrapKeepsSpace", testUnwrapKeepsSpace),
("testEmpty", testEmpty),
("testRemove", testRemove),
("testEq", testEq),
("testIs", testIs),
("testParents", testParents),
("testNot", testNot),
("testTagNameSet", testTagNameSet),
("testTraverse", testTraverse),
("testForms", testForms),
("testClassWithHyphen", testClassWithHyphen)
]
}()
}

View File

@ -15,15 +15,15 @@ class EntitiesTest: XCTestCase {
func testEscape()throws {
let text = "Hello &<> Å å π 新 there ¾ © »"
let escapedAscii = Entities.escape(text,OutputSettings().encoder(String.Encoding.ascii).escapeMode(Entities.EscapeMode.base))
let escapedAscii = Entities.escape(text, OutputSettings().encoder(String.Encoding.ascii).escapeMode(Entities.EscapeMode.base))
let escapedAsciiFull = Entities.escape(text, OutputSettings().charset(String.Encoding.ascii).escapeMode(Entities.EscapeMode.extended))
let escapedAsciiXhtml = Entities.escape(text, OutputSettings().charset(String.Encoding.ascii).escapeMode(Entities.EscapeMode.xhtml))
let escapedUtfFull = Entities.escape(text, OutputSettings().charset(String.Encoding.utf8).escapeMode(Entities.EscapeMode.extended))
let escapedUtfMin = Entities.escape(text, OutputSettings().charset(String.Encoding.utf8).escapeMode(Entities.EscapeMode.xhtml))
XCTAssertEqual("Hello &amp;&lt;&gt; &Aring; &aring; &#x3c0; &#x65b0; there &frac34; &copy; &raquo;", escapedAscii);
XCTAssertEqual("Hello &amp;&lt;&gt; &angst; &aring; &pi; &#x65b0; there &frac34; &copy; &raquo;", escapedAsciiFull);
XCTAssertEqual("Hello &amp;&lt;&gt; &#xc5; &#xe5; &#x3c0; &#x65b0; there &#xbe; &#xa9; &#xbb;", escapedAsciiXhtml);
XCTAssertEqual("Hello &amp;&lt;&gt; &Aring; &aring; &#x3c0; &#x65b0; there &frac34; &copy; &raquo;", escapedAscii)
XCTAssertEqual("Hello &amp;&lt;&gt; &angst; &aring; &pi; &#x65b0; there &frac34; &copy; &raquo;", escapedAsciiFull)
XCTAssertEqual("Hello &amp;&lt;&gt; &#xc5; &#xe5; &#x3c0; &#x65b0; there &#xbe; &#xa9; &#xbb;", escapedAsciiXhtml)
XCTAssertEqual("Hello &amp;&lt;&gt; Å å π 新 there ¾ © »", escapedUtfFull)
XCTAssertEqual("Hello &amp;&lt;&gt; Å å π 新 there ¾ © »", escapedUtfMin)
// odd that it's defined as aring in base but angst in full
@ -36,8 +36,6 @@ class EntitiesTest: XCTestCase {
XCTAssertEqual(text, try Entities.unescape(escapedUtfMin))
}
func testXhtml() {
//let text = "&amp; &gt; &lt; &quot;";
XCTAssertEqual(38, Entities.EscapeMode.xhtml.codepointForName("amp"))
@ -92,7 +90,6 @@ class EntitiesTest: XCTestCase {
XCTAssertEqual("Hello &= &", try Entities.unescape(string: text, strict: false))
}
func testCaseSensitive()throws {
let unescaped: String = "Ü ü & &"
XCTAssertEqual("&Uuml; &uuml; &amp; &amp;",
@ -128,7 +125,6 @@ class EntitiesTest: XCTestCase {
func testUscapesGtInXmlAttributesButNotInHtml()throws {
// https://github.com/jhy/jsoup/issues/528 - < is OK in HTML attribute values, but not in XML
let docHtml: String = "<a title='<p>One</p>'>One</a>"
let doc: Document = try SwiftSoup.parse(docHtml)
let element: Element = try doc.select("a").first()!
@ -137,25 +133,24 @@ class EntitiesTest: XCTestCase {
XCTAssertEqual("<a title=\"<p>One</p>\">One</a>", try element.outerHtml())
doc.outputSettings().escapeMode(Entities.EscapeMode.xhtml)
XCTAssertEqual("<a title=\"&lt;p>One&lt;/p>\">One</a>",try element.outerHtml())
XCTAssertEqual("<a title=\"&lt;p>One&lt;/p>\">One</a>", try element.outerHtml())
}
static var allTests = {
return [
("testEscape" , testEscape),
("testXhtml" , testXhtml),
("testGetByName" , testGetByName),
("testEscapeSupplementaryCharacter" , testEscapeSupplementaryCharacter),
("testNotMissingMultis" , testNotMissingMultis),
("testnotMissingSupplementals" , testnotMissingSupplementals),
("testUnescape" , testUnescape),
("testStrictUnescape" , testStrictUnescape),
("testCaseSensitive" , testCaseSensitive),
("testQuoteReplacements" , testQuoteReplacements),
("testLetterDigitEntities" , testLetterDigitEntities),
("testNoSpuriousDecodes" , testNoSpuriousDecodes),
("testUscapesGtInXmlAttributesButNotInHtml" , testUscapesGtInXmlAttributesButNotInHtml)
("testEscape", testEscape),
("testXhtml", testXhtml),
("testGetByName", testGetByName),
("testEscapeSupplementaryCharacter", testEscapeSupplementaryCharacter),
("testNotMissingMultis", testNotMissingMultis),
("testnotMissingSupplementals", testnotMissingSupplementals),
("testUnescape", testUnescape),
("testStrictUnescape", testStrictUnescape),
("testCaseSensitive", testCaseSensitive),
("testQuoteReplacements", testQuoteReplacements),
("testLetterDigitEntities", testLetterDigitEntities),
("testNoSpuriousDecodes", testNoSpuriousDecodes),
("testUscapesGtInXmlAttributesButNotInHtml", testUscapesGtInXmlAttributesButNotInHtml)
]
}()
}

View File

@ -73,7 +73,6 @@ class FormElementTest: XCTestCase {
// assertEquals("http://example.com/", con.request().url().toExternalForm());
// }
//TODO:
// @Test public void actionWithNoBaseUri() {
// String html = "<form><input name='q'></form>";
@ -152,9 +151,9 @@ class FormElementTest: XCTestCase {
static var allTests = {
return [
("testHasAssociatedControls" , testHasAssociatedControls),
("testFormsAddedAfterParseAreFormElements" , testFormsAddedAfterParseAreFormElements),
("testControlsAddedAfterParseAreLinkedWithForms" , testControlsAddedAfterParseAreLinkedWithForms)
("testHasAssociatedControls", testHasAssociatedControls),
("testFormsAddedAfterParseAreFormElements", testFormsAddedAfterParseAreFormElements),
("testControlsAddedAfterParseAreLinkedWithForms", testControlsAddedAfterParseAreLinkedWithForms)
]
}()
}

View File

@ -121,11 +121,11 @@ class HtmlParserTest: XCTestCase {
XCTAssertEqual(3, head.children().size())
XCTAssertEqual(1, body.children().size())
XCTAssertEqual("keywords",try head.getElementsByTag("meta").get(0).attr("name"))
XCTAssertEqual("keywords", try head.getElementsByTag("meta").get(0).attr("name"))
XCTAssertEqual(0, try body.getElementsByTag("meta").size())
XCTAssertEqual("jsoup",try doc.title())
XCTAssertEqual("Hello world",try body.text())
XCTAssertEqual("Hello world",try body.children().get(0).text())
XCTAssertEqual("jsoup", try doc.title())
XCTAssertEqual("Hello world", try body.text())
XCTAssertEqual("Hello world", try body.children().get(0).text())
}
func testCreatesStructureFromBodySnippet()throws {
@ -133,7 +133,7 @@ class HtmlParserTest: XCTestCase {
// needs to move into the start of the body
let html = "foo <b>bar</b> baz"
let doc = try SwiftSoup.parse(html)
XCTAssertEqual("foo bar baz",try doc.text())
XCTAssertEqual("foo bar baz", try doc.text())
}
@ -167,8 +167,8 @@ class HtmlParserTest: XCTestCase {
func testHandlesTextArea()throws {
let doc: Document = try SwiftSoup.parse("<textarea>Hello</textarea>")
let els: Elements = try doc.select("textarea")
XCTAssertEqual("Hello",try els.text())
XCTAssertEqual("Hello",try els.val())
XCTAssertEqual("Hello", try els.text())
XCTAssertEqual("Hello", try els.val())
}
func testPreservesSpaceInTextArea()throws {
@ -179,7 +179,7 @@ class HtmlParserTest: XCTestCase {
XCTAssertEqual(expect, try el.text())
XCTAssertEqual(expect, try el.val())
XCTAssertEqual(expect, try el.html())
XCTAssertEqual("<textarea>\n\t" + expect + "\n</textarea>",try el.outerHtml()) // but preserved in round-trip html
XCTAssertEqual("<textarea>\n\t" + expect + "\n</textarea>", try el.outerHtml()) // but preserved in round-trip html
}
func testPreservesSpaceInScript()throws {
@ -363,7 +363,7 @@ class HtmlParserTest: XCTestCase {
XCTAssertEqual(4, try doc.select("dt, dd").size())
let dts: Elements = try doc.select("dt")
XCTAssertEqual(2, dts.size())
XCTAssertEqual("Zug",try dts.get(1).nextElementSibling()?.text())
XCTAssertEqual("Zug", try dts.get(1).nextElementSibling()?.text())
}
func testHandlesBlocksInDefinitions()throws {
@ -372,7 +372,7 @@ class HtmlParserTest: XCTestCase {
let doc = try SwiftSoup.parse(h)
XCTAssertEqual("dt", try doc.select("#1").first()!.parent()!.tagName())
XCTAssertEqual("dd", try doc.select("#2").first()!.parent()!.tagName())
XCTAssertEqual("<dl><dt><div id=\"1\">Term</div></dt><dd><div id=\"2\">Def</div></dd></dl>",try TextUtil.stripNewlines(doc.body()!.html()))
XCTAssertEqual("<dl><dt><div id=\"1\">Term</div></dt><dd><div id=\"2\">Def</div></dd></dl>", try TextUtil.stripNewlines(doc.body()!.html()))
}
func testHandlesFrames()throws {
@ -386,7 +386,7 @@ class HtmlParserTest: XCTestCase {
func testIgnoresContentAfterFrameset()throws {
let h = "<html><head><title>One</title></head><frameset><frame /><frame /></frameset><table></table></html>"
let doc = try SwiftSoup.parse(h)
XCTAssertEqual("<html><head><title>One</title></head><frameset><frame><frame></frameset></html>",try TextUtil.stripNewlines(doc.html()))
XCTAssertEqual("<html><head><title>One</title></head><frameset><frame><frame></frameset></html>", try TextUtil.stripNewlines(doc.html()))
// no body, no table. No crash!
}
@ -404,7 +404,7 @@ class HtmlParserTest: XCTestCase {
let doc = try SwiftSoup.parse(h, "http://example.com/")
let a: Element = try doc.select("a").first()!
XCTAssertEqual("/foo", try a.attr("href"))
XCTAssertEqual("http://example.com/foo",try a.attr("abs:href"))
XCTAssertEqual("http://example.com/foo", try a.attr("abs:href"))
}
func testNormalisesDocument()throws {
@ -444,43 +444,43 @@ class HtmlParserTest: XCTestCase {
XCTAssertEqual("<h1>Hello </h1><h2>There <hgroup><h1>Another</h1><h2>headline</h2></hgroup> <hgroup><h1>More</h1><p>stuff</p></hgroup></h2>", try TextUtil.stripNewlines(doc.body()!.html()))
}
func testRelaxedTags()throws{
func testRelaxedTags()throws {
let doc = try SwiftSoup.parse("<abc_def id=1>Hello</abc_def> <abc-def>There</abc-def>")
XCTAssertEqual("<abc_def id=\"1\">Hello</abc_def> <abc-def>There</abc-def>", try TextUtil.stripNewlines(doc.body()!.html()))
}
func testHeaderContents()throws{
func testHeaderContents()throws {
// h* tags (h1 .. h9) in browsers can handle any internal content other than other h*. which is not per any
// spec, which defines them as containing phrasing content only. so, reality over theory.
let doc = try SwiftSoup.parse("<h1>Hello <div>There</div> now</h1> <h2>More <h3>Content</h3></h2>")
XCTAssertEqual("<h1>Hello <div>There</div> now</h1> <h2>More </h2><h3>Content</h3>", try TextUtil.stripNewlines(doc.body()!.html()))
}
func testSpanContents()throws{
func testSpanContents()throws {
// like h1 tags, the spec says SPAN is phrasing only, but browsers and publisher treat span as a block tag
let doc = try SwiftSoup.parse("<span>Hello <div>there</div> <span>now</span></span>")
XCTAssertEqual("<span>Hello <div>there</div> <span>now</span></span>", try TextUtil.stripNewlines(doc.body()!.html()))
}
func testNoImagesInNoScriptInHead()throws{
func testNoImagesInNoScriptInHead()throws {
// jsoup used to allow, but against spec if parsing with noscript
let doc = try SwiftSoup.parse("<html><head><noscript><img src='foo'></noscript></head><body><p>Hello</p></body></html>")
XCTAssertEqual("<html><head><noscript>&lt;img src=\"foo\"&gt;</noscript></head><body><p>Hello</p></body></html>", try TextUtil.stripNewlines(doc.html()))
}
func testAFlowContents()throws{
func testAFlowContents()throws {
// html5 has <a> as either phrasing or block
let doc = try SwiftSoup.parse("<a>Hello <div>there</div> <span>now</span></a>")
XCTAssertEqual("<a>Hello <div>there</div> <span>now</span></a>", try TextUtil.stripNewlines(doc.body()!.html()))
}
func testFontFlowContents()throws{
func testFontFlowContents()throws {
// html5 has no definition of <font>; often used as flow
let doc = try SwiftSoup.parse("<font>Hello <div>there</div> <span>now</span></font>")
XCTAssertEqual("<font>Hello <div>there</div> <span>now</span></font>", try TextUtil.stripNewlines(doc.body()!.html()))
}
func testhandlesMisnestedTagsBI()throws{
func testhandlesMisnestedTagsBI()throws {
// whatwg: <b><i></b></i>
let h = "<p>1<b>2<i>3</b>4</i>5</p>"
let doc = try SwiftSoup.parse(h)
@ -488,7 +488,7 @@ class HtmlParserTest: XCTestCase {
// adoption agency on </b>, reconstruction of formatters on 4.
}
func testhandlesMisnestedTagsBP()throws{
func testhandlesMisnestedTagsBP()throws {
// whatwg: <b><p></b></p>
let h = "<b>1<p>2</b>3</p>"
let doc = try SwiftSoup.parse(h)
@ -587,73 +587,73 @@ class HtmlParserTest: XCTestCase {
static var allTests = {
return [
("testParsesSimpleDocument" , testParsesSimpleDocument),
("testParsesRoughAttributes" , testParsesRoughAttributes),
("testParsesQuiteRoughAttributes" , testParsesQuiteRoughAttributes),
("testParsesComments" , testParsesComments),
("testParsesUnterminatedComments" , testParsesUnterminatedComments),
("testDropsUnterminatedTag" , testDropsUnterminatedTag),
("testDropsUnterminatedAttribute" , testDropsUnterminatedAttribute),
("testParsesUnterminatedTextarea" , testParsesUnterminatedTextarea),
("testParsesUnterminatedOption" , testParsesUnterminatedOption),
("testSpaceAfterTag" , testSpaceAfterTag),
("testCreatesDocumentStructure" , testCreatesDocumentStructure),
("testCreatesStructureFromBodySnippet" , testCreatesStructureFromBodySnippet),
("testHandlesEscapedData" , testHandlesEscapedData),
("testHandlesDataOnlyTags" , testHandlesDataOnlyTags),
("testHandlesTextAfterData" , testHandlesTextAfterData),
("testHandlesTextArea" , testHandlesTextArea),
("testPreservesSpaceInTextArea" , testPreservesSpaceInTextArea),
("testPreservesSpaceInScript" , testPreservesSpaceInScript),
("testDoesNotCreateImplicitLists" , testDoesNotCreateImplicitLists),
("testDiscardsNakedTds" , testDiscardsNakedTds),
("testHandlesNestedImplicitTable" , testHandlesNestedImplicitTable),
("testHandlesWhatWgExpensesTableExample" , testHandlesWhatWgExpensesTableExample),
("testHandlesTbodyTable" , testHandlesTbodyTable),
("testHandlesImplicitCaptionClose" , testHandlesImplicitCaptionClose),
("testNoTableDirectInTable" , testNoTableDirectInTable),
("testIgnoresDupeEndTrTag" , testIgnoresDupeEndTrTag),
("testHandlesBaseTags" , testHandlesBaseTags),
("testHandlesProtocolRelativeUrl" , testHandlesProtocolRelativeUrl),
("testHandlesCdata" , testHandlesCdata),
("testHandlesUnclosedCdataAtEOF" , testHandlesUnclosedCdataAtEOF),
("testHandlesInvalidStartTags" , testHandlesInvalidStartTags),
("testHandlesUnknownTags" , testHandlesUnknownTags),
("testHandlesUnknownInlineTags" , testHandlesUnknownInlineTags),
("testParsesBodyFragment" , testParsesBodyFragment),
("testHandlesUnknownNamespaceTags" , testHandlesUnknownNamespaceTags),
("testHandlesKnownEmptyBlocks" , testHandlesKnownEmptyBlocks),
("testHandlesSolidusAtAttributeEnd" , testHandlesSolidusAtAttributeEnd),
("testHandlesMultiClosingBody" , testHandlesMultiClosingBody),
("testHandlesUnclosedDefinitionLists" , testHandlesUnclosedDefinitionLists),
("testHandlesBlocksInDefinitions" , testHandlesBlocksInDefinitions),
("testHandlesFrames" , testHandlesFrames),
("testIgnoresContentAfterFrameset" , testIgnoresContentAfterFrameset),
("testHandlesJavadocFont" , testHandlesJavadocFont),
("testHandlesBaseWithoutHref" , testHandlesBaseWithoutHref),
("testNormalisesDocument" , testNormalisesDocument),
("testNormalisesEmptyDocument" , testNormalisesEmptyDocument),
("testNormalisesHeadlessBody" , testNormalisesHeadlessBody),
("testNormalisedBodyAfterContent" , testNormalisedBodyAfterContent),
("testfindsCharsetInMalformedMeta" , testfindsCharsetInMalformedMeta),
("testHgroup" , testHgroup),
("testRelaxedTags" , testRelaxedTags),
("testHeaderContents" , testHeaderContents),
("testSpanContents" , testSpanContents),
("testNoImagesInNoScriptInHead" , testNoImagesInNoScriptInHead),
("testAFlowContents" , testAFlowContents),
("testFontFlowContents" , testFontFlowContents),
("testhandlesMisnestedTagsBI" , testhandlesMisnestedTagsBI),
("testhandlesMisnestedTagsBP" , testhandlesMisnestedTagsBP),
("testhandlesUnexpectedMarkupInTables" , testhandlesUnexpectedMarkupInTables),
("testHandlesUnclosedFormattingElements" , testHandlesUnclosedFormattingElements),
("testhandlesUnclosedAnchors" , testhandlesUnclosedAnchors),
("testreconstructFormattingElements" , testreconstructFormattingElements),
("testreconstructFormattingElementsInTable" , testreconstructFormattingElementsInTable),
("testcommentBeforeHtml" , testcommentBeforeHtml),
("testemptyTdTag" , testemptyTdTag),
("testhandlesSolidusInA" , testhandlesSolidusInA),
("testhandlesSpanInTbody" , testhandlesSpanInTbody)
("testParsesSimpleDocument", testParsesSimpleDocument),
("testParsesRoughAttributes", testParsesRoughAttributes),
("testParsesQuiteRoughAttributes", testParsesQuiteRoughAttributes),
("testParsesComments", testParsesComments),
("testParsesUnterminatedComments", testParsesUnterminatedComments),
("testDropsUnterminatedTag", testDropsUnterminatedTag),
("testDropsUnterminatedAttribute", testDropsUnterminatedAttribute),
("testParsesUnterminatedTextarea", testParsesUnterminatedTextarea),
("testParsesUnterminatedOption", testParsesUnterminatedOption),
("testSpaceAfterTag", testSpaceAfterTag),
("testCreatesDocumentStructure", testCreatesDocumentStructure),
("testCreatesStructureFromBodySnippet", testCreatesStructureFromBodySnippet),
("testHandlesEscapedData", testHandlesEscapedData),
("testHandlesDataOnlyTags", testHandlesDataOnlyTags),
("testHandlesTextAfterData", testHandlesTextAfterData),
("testHandlesTextArea", testHandlesTextArea),
("testPreservesSpaceInTextArea", testPreservesSpaceInTextArea),
("testPreservesSpaceInScript", testPreservesSpaceInScript),
("testDoesNotCreateImplicitLists", testDoesNotCreateImplicitLists),
("testDiscardsNakedTds", testDiscardsNakedTds),
("testHandlesNestedImplicitTable", testHandlesNestedImplicitTable),
("testHandlesWhatWgExpensesTableExample", testHandlesWhatWgExpensesTableExample),
("testHandlesTbodyTable", testHandlesTbodyTable),
("testHandlesImplicitCaptionClose", testHandlesImplicitCaptionClose),
("testNoTableDirectInTable", testNoTableDirectInTable),
("testIgnoresDupeEndTrTag", testIgnoresDupeEndTrTag),
("testHandlesBaseTags", testHandlesBaseTags),
("testHandlesProtocolRelativeUrl", testHandlesProtocolRelativeUrl),
("testHandlesCdata", testHandlesCdata),
("testHandlesUnclosedCdataAtEOF", testHandlesUnclosedCdataAtEOF),
("testHandlesInvalidStartTags", testHandlesInvalidStartTags),
("testHandlesUnknownTags", testHandlesUnknownTags),
("testHandlesUnknownInlineTags", testHandlesUnknownInlineTags),
("testParsesBodyFragment", testParsesBodyFragment),
("testHandlesUnknownNamespaceTags", testHandlesUnknownNamespaceTags),
("testHandlesKnownEmptyBlocks", testHandlesKnownEmptyBlocks),
("testHandlesSolidusAtAttributeEnd", testHandlesSolidusAtAttributeEnd),
("testHandlesMultiClosingBody", testHandlesMultiClosingBody),
("testHandlesUnclosedDefinitionLists", testHandlesUnclosedDefinitionLists),
("testHandlesBlocksInDefinitions", testHandlesBlocksInDefinitions),
("testHandlesFrames", testHandlesFrames),
("testIgnoresContentAfterFrameset", testIgnoresContentAfterFrameset),
("testHandlesJavadocFont", testHandlesJavadocFont),
("testHandlesBaseWithoutHref", testHandlesBaseWithoutHref),
("testNormalisesDocument", testNormalisesDocument),
("testNormalisesEmptyDocument", testNormalisesEmptyDocument),
("testNormalisesHeadlessBody", testNormalisesHeadlessBody),
("testNormalisedBodyAfterContent", testNormalisedBodyAfterContent),
("testfindsCharsetInMalformedMeta", testfindsCharsetInMalformedMeta),
("testHgroup", testHgroup),
("testRelaxedTags", testRelaxedTags),
("testHeaderContents", testHeaderContents),
("testSpanContents", testSpanContents),
("testNoImagesInNoScriptInHead", testNoImagesInNoScriptInHead),
("testAFlowContents", testAFlowContents),
("testFontFlowContents", testFontFlowContents),
("testhandlesMisnestedTagsBI", testhandlesMisnestedTagsBI),
("testhandlesMisnestedTagsBP", testhandlesMisnestedTagsBP),
("testhandlesUnexpectedMarkupInTables", testhandlesUnexpectedMarkupInTables),
("testHandlesUnclosedFormattingElements", testHandlesUnclosedFormattingElements),
("testhandlesUnclosedAnchors", testhandlesUnclosedAnchors),
("testreconstructFormattingElements", testreconstructFormattingElements),
("testreconstructFormattingElementsInTable", testreconstructFormattingElementsInTable),
("testcommentBeforeHtml", testcommentBeforeHtml),
("testemptyTdTag", testemptyTdTag),
("testhandlesSolidusInA", testhandlesSolidusInA),
("testhandlesSpanInTbody", testhandlesSpanInTbody)
]
}()

View File

@ -12,7 +12,7 @@ import SwiftSoup
class NodeTest: XCTestCase {
func testHandlesBaseUri() {
do{
do {
let tag: Tag = try Tag.valueOf("a")
let attribs: Attributes = Attributes()
try attribs.put("relHref", "/foo")
@ -31,14 +31,14 @@ class NodeTest: XCTestCase {
XCTAssertEqual("http://bar/qux", try dodgyBase.absUrl("absHref")) // base fails, but href good, so get that
//TODO:Nabil in swift an url with scheme wtf is valid , find a method to validate schemes
//XCTAssertEqual("", try dodgyBase.absUrl("relHref")); // base fails, only rel href, so return nothing
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
func testSetBaseUriIsRecursive() {
do{
do {
let doc: Document = try SwiftSoup.parse("<div><p></p></div>")
let baseUri: String = "https://jsoup.org"
try doc.setBaseUri(baseUri)
@ -46,37 +46,37 @@ class NodeTest: XCTestCase {
XCTAssertEqual(baseUri, doc.getBaseUri())
XCTAssertEqual(baseUri, try doc.select("div").first()?.getBaseUri())
XCTAssertEqual(baseUri, try doc.select("p").first()?.getBaseUri())
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
func testHandlesAbsPrefix() {
do{
do {
let doc: Document = try SwiftSoup.parse("<a href=/foo>Hello</a>", "https://jsoup.org/")
let a: Element? = try doc.select("a").first()
XCTAssertEqual("/foo", try a?.attr("href"))
XCTAssertEqual("https://jsoup.org/foo", try a?.attr("abs:href"))
//XCTAssertTrue(a!.hasAttr("abs:href"));//TODO:nabil
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
func testHandlesAbsOnImage() {
do{
do {
let doc: Document = try SwiftSoup.parse("<p><img src=\"/rez/osi_logo.png\" /></p>", "https://jsoup.org/")
let img: Element? = try doc.select("img").first()
XCTAssertEqual("https://jsoup.org/rez/osi_logo.png", try img?.attr("abs:src"))
XCTAssertEqual(try img?.absUrl("src"), try img?.attr("abs:src"))
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
func testHandlesAbsPrefixOnHasAttr() {
do{
do {
// 1: no abs url; 2: has abs url
let doc: Document = try SwiftSoup.parse("<a id=1 href='/foo'>One</a> <a id=2 href='https://jsoup.org/'>Two</a>")
let one: Element = try doc.select("#1").first()!
@ -89,22 +89,21 @@ class NodeTest: XCTestCase {
XCTAssertTrue(two.hasAttr("abs:href"))
XCTAssertTrue(two.hasAttr("href"))
XCTAssertEqual("https://jsoup.org/", try two.absUrl("href"))
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
func testLiteralAbsPrefix() {
do{
do {
// if there is a literal attribute "abs:xxx", don't try and make absolute.
let doc: Document = try SwiftSoup.parse("<a abs:href='odd'>One</a>")
let el: Element = try doc.select("a").first()!
XCTAssertTrue(el.hasAttr("abs:href"))
XCTAssertEqual("odd", try el.attr("abs:href"))
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
@ -123,17 +122,17 @@ class NodeTest: XCTestCase {
}
*/
func testHandleAbsOnLocalhostFileUris() {
do{
do {
let doc: Document = try SwiftSoup.parse("<a href='password'>One/a><a href='/var/log/messages'>Two</a>", "file://localhost/etc/")
let one: Element? = try doc.select("a").first()
XCTAssertEqual("file://localhost/etc/password", try one?.absUrl("href"))
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
func testHandlesAbsOnProtocolessAbsoluteUris() {
do{
do {
let doc1: Document = try SwiftSoup.parse("<a href='//example.net/foo'>One</a>", "http://example.com/")
let doc2: Document = try SwiftSoup.parse("<a href='//example.net/foo'>One</a>", "https://example.com/")
@ -145,13 +144,13 @@ class NodeTest: XCTestCase {
let doc3: Document = try SwiftSoup.parse("<img src=//www.google.com/images/errors/logo_sm.gif alt=Google>", "https://google.com")
XCTAssertEqual("https://www.google.com/images/errors/logo_sm.gif", try doc3.select("img").attr("abs:src"))
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
func testAbsHandlesRelativeQuery() {
do{
do {
let doc: Document = try SwiftSoup.parse("<a href='?foo'>One</a> <a href='bar.html?foo'>Two</a>", "https://jsoup.org/path/file?bar")
let a1: Element? = try doc.select("a").first()
@ -159,61 +158,61 @@ class NodeTest: XCTestCase {
let a2: Element? = try doc.select("a").get(1)
XCTAssertEqual("https://jsoup.org/path/bar.html?foo", try a2?.absUrl("href"))
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
func testAbsHandlesDotFromIndex() {
do{
do {
let doc: Document = try SwiftSoup.parse("<a href='./one/two.html'>One</a>", "http://example.com")
let a1: Element? = try doc.select("a").first()
XCTAssertEqual("http://example.com/one/two.html", try a1?.absUrl("href"))
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
func testRemove() {
do{
do {
let doc: Document = try SwiftSoup.parse("<p>One <span>two</span> three</p>")
let p: Element? = try doc.select("p").first()
try p?.childNode(0).remove()
XCTAssertEqual("two three", try p?.text())
XCTAssertEqual("<span>two</span> three", TextUtil.stripNewlines(try p!.html()))
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
func testReplace() {
do{
do {
let doc: Document = try SwiftSoup.parse("<p>One <span>two</span> three</p>")
let p: Element? = try doc.select("p").first()
let insert: Element = try doc.createElement("em").text("foo")
try p?.childNode(1).replaceWith(insert)
XCTAssertEqual("One <em>foo</em> three", try p?.html())
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
func testOwnerDocument() {
do{
do {
let doc: Document = try SwiftSoup.parse("<p>Hello")
let p: Element? = try doc.select("p").first()
XCTAssertTrue(p?.ownerDocument() == doc)
XCTAssertTrue(doc.ownerDocument() == doc)
XCTAssertNil(doc.parent())
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
func testBefore() {
do{
do {
let doc: Document = try SwiftSoup.parse("<p>One <b>two</b> three</p>")
let newNode: Element = Element(try Tag.valueOf("em"), "")
try newNode.appendText("four")
@ -223,13 +222,13 @@ class NodeTest: XCTestCase {
try doc.select("b").first()?.before("<i>five</i>")
XCTAssertEqual("<p>One <em>four</em><i>five</i><b>two</b> three</p>", try doc.body()?.html())
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
func testAfter() {
do{
do {
let doc: Document = try SwiftSoup.parse("<p>One <b>two</b> three</p>")
let newNode: Element = Element(try Tag.valueOf("em"), "")
try newNode.appendText("four")
@ -239,14 +238,14 @@ class NodeTest: XCTestCase {
try doc.select("b").first()?.after("<i>five</i>")
XCTAssertEqual("<p>One <b>two</b><i>five</i><em>four</em> three</p>", try doc.body()?.html())
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
func testUnwrap() {
do{
do {
let doc: Document = try SwiftSoup.parse("<div>One <span>Two <b>Three</b></span> Four</div>")
let span: Element? = try doc.select("span").first()
let twoText: Node? = span?.childNode(0)
@ -257,49 +256,49 @@ class NodeTest: XCTestCase {
XCTAssertEqual("Two ", (node as? TextNode)?.text())
XCTAssertEqual(node, twoText)
XCTAssertEqual(node?.parent(), try doc.select("div").first())
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
func testUnwrapNoChildren() {
do{
do {
let doc: Document = try SwiftSoup.parse("<div>One <span></span> Two</div>")
let span: Element? = try doc.select("span").first()
let node: Node? = try span?.unwrap()
XCTAssertEqual("<div>One Two</div>", TextUtil.stripNewlines(try doc.body()!.html()))
XCTAssertTrue(node == nil)
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
func testTraverse() {
do{
do {
let doc: Document = try SwiftSoup.parse("<div><p>Hello</p></div><div>There</div>")
let accum: StringBuilder = StringBuilder()
class nv : NodeVisitor{
class nv: NodeVisitor {
let accum: StringBuilder
init (_ accum: StringBuilder){
init (_ accum: StringBuilder) {
self.accum = accum
}
func head(_ node: Node, _ depth: Int)throws{
func head(_ node: Node, _ depth: Int)throws {
accum.append("<" + node.nodeName() + ">")
}
func tail(_ node: Node, _ depth: Int)throws{
func tail(_ node: Node, _ depth: Int)throws {
accum.append("</" + node.nodeName() + ">")
}
}
try doc.select("div").first()?.traverse(nv(accum))
XCTAssertEqual("<div><p><#text></#text></p></div>", accum.toString())
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
func testOrphanNodeReturnsNullForSiblingElements() {
do{
do {
let node: Node = Element(try Tag.valueOf("p"), "")
let el: Element = Element(try Tag.valueOf("p"), "")
@ -312,13 +311,13 @@ class NodeTest: XCTestCase {
XCTAssertEqual(0, el.siblingElements().size())
XCTAssertNil(try el.previousElementSibling())
XCTAssertNil(try el.nextElementSibling())
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
func testNodeIsNotASiblingOfItself() {
do{
do {
let doc: Document = try SwiftSoup.parse("<div><p>One<p>Two<p>Three</div>")
let p2: Element = try doc.select("p").get(1)
@ -327,13 +326,13 @@ class NodeTest: XCTestCase {
XCTAssertEqual(2, nodes.count)
XCTAssertEqual("<p>One</p>", try nodes[0].outerHtml())
XCTAssertEqual("<p>Three</p>", try nodes[1].outerHtml())
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
func testChildNodesCopy() {
do{
do {
let doc: Document = try SwiftSoup.parse("<div id=1>Text 1 <p>One</p> Text 2 <p>Two<p>Three</div><div id=2>")
let div1: Element? = try doc.select("#1").first()
let div2: Element? = try doc.select("#2").first()
@ -345,13 +344,13 @@ class NodeTest: XCTestCase {
XCTAssertEqual("Text 1 ", tn1?.text())
try div2?.insertChildren(-1, divChildren!)
XCTAssertEqual("<div id=\"1\">Text 1 <p>One</p> Text 2 <p>Two</p><p>Three</p></div><div id=\"2\">Text 1 updated"+"<p>One</p> Text 2 <p>Two</p><p>Three</p></div>", TextUtil.stripNewlines(try doc.body()!.html()))
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
func testSupportsClone() {
do{
do {
let doc: Document = try SwiftSoup.parse("<div class=foo>Text</div>")
let el: Element = try doc.select("div").first()!
XCTAssertTrue(el.hasClass("foo"))
@ -366,35 +365,35 @@ class NodeTest: XCTestCase {
XCTAssertTrue(elClone.hasClass("foo"))
XCTAssertTrue(try el.text() == "None")
XCTAssertTrue(try elClone.text()=="Text")
}catch{
XCTAssertEqual(1,2)
} catch {
XCTAssertEqual(1, 2)
}
}
static var allTests = {
return [
("testHandlesBaseUri" , testHandlesBaseUri),
("testSetBaseUriIsRecursive" , testSetBaseUriIsRecursive),
("testHandlesAbsPrefix" , testHandlesAbsPrefix),
("testHandlesAbsOnImage" , testHandlesAbsOnImage),
("testHandlesAbsPrefixOnHasAttr" , testHandlesAbsPrefixOnHasAttr),
("testLiteralAbsPrefix" , testLiteralAbsPrefix),
("testHandleAbsOnLocalhostFileUris" , testHandleAbsOnLocalhostFileUris),
("testHandlesAbsOnProtocolessAbsoluteUris" , testHandlesAbsOnProtocolessAbsoluteUris),
("testAbsHandlesRelativeQuery" , testAbsHandlesRelativeQuery),
("testAbsHandlesDotFromIndex" , testAbsHandlesDotFromIndex),
("testRemove" , testRemove),
("testReplace" , testReplace),
("testOwnerDocument" , testOwnerDocument),
("testBefore" , testBefore),
("testAfter" , testAfter),
("testUnwrap" , testUnwrap),
("testUnwrapNoChildren" , testUnwrapNoChildren),
("testTraverse" , testTraverse),
("testOrphanNodeReturnsNullForSiblingElements" , testOrphanNodeReturnsNullForSiblingElements),
("testNodeIsNotASiblingOfItself" , testNodeIsNotASiblingOfItself),
("testChildNodesCopy" , testChildNodesCopy),
("testSupportsClone" , testSupportsClone)
("testHandlesBaseUri", testHandlesBaseUri),
("testSetBaseUriIsRecursive", testSetBaseUriIsRecursive),
("testHandlesAbsPrefix", testHandlesAbsPrefix),
("testHandlesAbsOnImage", testHandlesAbsOnImage),
("testHandlesAbsPrefixOnHasAttr", testHandlesAbsPrefixOnHasAttr),
("testLiteralAbsPrefix", testLiteralAbsPrefix),
("testHandleAbsOnLocalhostFileUris", testHandleAbsOnLocalhostFileUris),
("testHandlesAbsOnProtocolessAbsoluteUris", testHandlesAbsOnProtocolessAbsoluteUris),
("testAbsHandlesRelativeQuery", testAbsHandlesRelativeQuery),
("testAbsHandlesDotFromIndex", testAbsHandlesDotFromIndex),
("testRemove", testRemove),
("testReplace", testReplace),
("testOwnerDocument", testOwnerDocument),
("testBefore", testBefore),
("testAfter", testAfter),
("testUnwrap", testUnwrap),
("testUnwrapNoChildren", testUnwrapNoChildren),
("testTraverse", testTraverse),
("testOrphanNodeReturnsNullForSiblingElements", testOrphanNodeReturnsNullForSiblingElements),
("testNodeIsNotASiblingOfItself", testNodeIsNotASiblingOfItself),
("testChildNodesCopy", testChildNodesCopy),
("testSupportsClone", testSupportsClone)
]
}()
}

View File

@ -32,7 +32,7 @@ class ParseSettingsTest: XCTestCase {
static var allTests = {
return [
("testCaseSupport" , testCaseSupport)
("testCaseSupport", testCaseSupport)
]
}()
}

View File

@ -18,7 +18,7 @@ class QueryParserTest: XCTestCase {
XCTAssertTrue((eval as? CombiningEvaluator.Or) != nil)
let or: CombiningEvaluator.Or = eval as! CombiningEvaluator.Or
XCTAssertEqual(3, or.evaluators.count)
for innerEval:Evaluator in or.evaluators {
for innerEval: Evaluator in or.evaluators {
XCTAssertTrue((innerEval as? CombiningEvaluator.And) != nil)
let and: CombiningEvaluator.And = innerEval as! CombiningEvaluator.And
XCTAssertEqual(2, and.evaluators.count)
@ -44,8 +44,8 @@ class QueryParserTest: XCTestCase {
static var allTests = {
return [
("testOrGetsCorrectPrecedence" , testOrGetsCorrectPrecedence),
("testParsesMultiCorrectly" , testParsesMultiCorrectly)
("testOrGetsCorrectPrecedence", testOrGetsCorrectPrecedence),
("testParsesMultiCorrectly", testParsesMultiCorrectly)
]
}()

View File

@ -695,58 +695,58 @@ class SelectorTest: XCTestCase {
static var allTests = {
return [
("testByTag" , testByTag),
("testById" , testById),
("testByClass" , testByClass),
("testByAttribute" , testByAttribute),
("testNamespacedTag" , testNamespacedTag),
("testWildcardNamespacedTag" , testWildcardNamespacedTag),
("testByAttributeStarting" , testByAttributeStarting),
("testByAttributeRegex" , testByAttributeRegex),
("testByAttributeRegexCharacterClass" , testByAttributeRegexCharacterClass),
("testByAttributeRegexCombined" , testByAttributeRegexCombined),
("testCombinedWithContains" , testCombinedWithContains),
("testAllElements" , testAllElements),
("testAllWithClass" , testAllWithClass),
("testGroupOr" , testGroupOr),
("testGroupOrAttribute" , testGroupOrAttribute),
("testDescendant" , testDescendant),
("testAnd" , testAnd),
("testDeeperDescendant" , testDeeperDescendant),
("testParentChildElement" , testParentChildElement),
("testParentWithClassChild" , testParentWithClassChild),
("testParentChildStar" , testParentChildStar),
("testMultiChildDescent" , testMultiChildDescent),
("testCaseInsensitive" , testCaseInsensitive),
("testAdjacentSiblings" , testAdjacentSiblings),
("testAdjacentSiblingsWithId" , testAdjacentSiblingsWithId),
("testNotAdjacent" , testNotAdjacent),
("testMixCombinator" , testMixCombinator),
("testMixCombinatorGroup" , testMixCombinatorGroup),
("testGeneralSiblings" , testGeneralSiblings),
("testCharactersInIdAndClass" , testCharactersInIdAndClass),
("testSupportsLeadingCombinator" , testSupportsLeadingCombinator),
("testPseudoLessThan" , testPseudoLessThan),
("testPseudoGreaterThan" , testPseudoGreaterThan),
("testPseudoEquals" , testPseudoEquals),
("testPseudoBetween" , testPseudoBetween),
("testPseudoCombined" , testPseudoCombined),
("testPseudoHas" , testPseudoHas),
("testNestedHas" , testNestedHas),
("testPseudoContains" , testPseudoContains),
("testPsuedoContainsWithParentheses" , testPsuedoContainsWithParentheses),
("testContainsOwn" , testContainsOwn),
("testMatches" , testMatches),
("testMatchesOwn" , testMatchesOwn),
("testRelaxedTags" , testRelaxedTags),
("testNotParas" , testNotParas),
("testNotAll" , testNotAll),
("testNotClass" , testNotClass),
("testHandlesCommasInSelector" , testHandlesCommasInSelector),
("testSelectSupplementaryCharacter" , testSelectSupplementaryCharacter),
("testSelectClassWithSpace" , testSelectClassWithSpace),
("testSelectSameElements" , testSelectSameElements),
("testAttributeWithBrackets" , testAttributeWithBrackets)
("testByTag", testByTag),
("testById", testById),
("testByClass", testByClass),
("testByAttribute", testByAttribute),
("testNamespacedTag", testNamespacedTag),
("testWildcardNamespacedTag", testWildcardNamespacedTag),
("testByAttributeStarting", testByAttributeStarting),
("testByAttributeRegex", testByAttributeRegex),
("testByAttributeRegexCharacterClass", testByAttributeRegexCharacterClass),
("testByAttributeRegexCombined", testByAttributeRegexCombined),
("testCombinedWithContains", testCombinedWithContains),
("testAllElements", testAllElements),
("testAllWithClass", testAllWithClass),
("testGroupOr", testGroupOr),
("testGroupOrAttribute", testGroupOrAttribute),
("testDescendant", testDescendant),
("testAnd", testAnd),
("testDeeperDescendant", testDeeperDescendant),
("testParentChildElement", testParentChildElement),
("testParentWithClassChild", testParentWithClassChild),
("testParentChildStar", testParentChildStar),
("testMultiChildDescent", testMultiChildDescent),
("testCaseInsensitive", testCaseInsensitive),
("testAdjacentSiblings", testAdjacentSiblings),
("testAdjacentSiblingsWithId", testAdjacentSiblingsWithId),
("testNotAdjacent", testNotAdjacent),
("testMixCombinator", testMixCombinator),
("testMixCombinatorGroup", testMixCombinatorGroup),
("testGeneralSiblings", testGeneralSiblings),
("testCharactersInIdAndClass", testCharactersInIdAndClass),
("testSupportsLeadingCombinator", testSupportsLeadingCombinator),
("testPseudoLessThan", testPseudoLessThan),
("testPseudoGreaterThan", testPseudoGreaterThan),
("testPseudoEquals", testPseudoEquals),
("testPseudoBetween", testPseudoBetween),
("testPseudoCombined", testPseudoCombined),
("testPseudoHas", testPseudoHas),
("testNestedHas", testNestedHas),
("testPseudoContains", testPseudoContains),
("testPsuedoContainsWithParentheses", testPsuedoContainsWithParentheses),
("testContainsOwn", testContainsOwn),
("testMatches", testMatches),
("testMatchesOwn", testMatchesOwn),
("testRelaxedTags", testRelaxedTags),
("testNotParas", testNotParas),
("testNotAll", testNotAll),
("testNotClass", testNotClass),
("testHandlesCommasInSelector", testHandlesCommasInSelector),
("testSelectSupplementaryCharacter", testSelectSupplementaryCharacter),
("testSelectClassWithSpace", testSelectClassWithSpace),
("testSelectSameElements", testSelectSameElements),
("testAttributeWithBrackets", testAttributeWithBrackets)
]
}()

View File

@ -42,9 +42,9 @@ class StringUtilTest: XCTestCase {
// }
func testJoin() {
XCTAssertEqual("",StringUtil.join([""], sep: " "))
XCTAssertEqual("one",StringUtil.join(["one"], sep: " "))
XCTAssertEqual("one two three",StringUtil.join(["one", "two", "three"], sep: " "))
XCTAssertEqual("", StringUtil.join([""], sep: " "))
XCTAssertEqual("one", StringUtil.join(["one"], sep: " "))
XCTAssertEqual("one two three", StringUtil.join(["one", "two", "three"], sep: " "))
}
func testPadding() {
@ -75,7 +75,6 @@ class StringUtilTest: XCTestCase {
XCTAssertTrue(StringUtil.isNumeric("1234"))
}
func testIsWhitespace() {
XCTAssertTrue(StringUtil.isWhitespace("\t"))
XCTAssertTrue(StringUtil.isWhitespace("\n"))

View File

@ -72,15 +72,15 @@ class TagTest: XCTestCase {
static var allTests = {
return [
("testIsCaseSensitive" , testIsCaseSensitive),
("testCanBeInsensitive" , testCanBeInsensitive),
("testTrims" , testTrims),
("testEquality" , testEquality),
("testDivSemantics" , testDivSemantics),
("testPSemantics" , testPSemantics),
("testImgSemantics" , testImgSemantics),
("testDefaultSemantics" , testDefaultSemantics),
("testValueOfChecksNotEmpty" , testValueOfChecksNotEmpty),
("testIsCaseSensitive", testIsCaseSensitive),
("testCanBeInsensitive", testCanBeInsensitive),
("testTrims", testTrims),
("testEquality", testEquality),
("testDivSemantics", testDivSemantics),
("testPSemantics", testPSemantics),
("testImgSemantics", testImgSemantics),
("testDefaultSemantics", testDefaultSemantics),
("testValueOfChecksNotEmpty", testValueOfChecksNotEmpty),
]
}()
}

View File

@ -27,7 +27,7 @@ class TextNodeTest: XCTestCase {
func testTextBean()throws {
let doc = try SwiftSoup.parse("<p>One <span>two &amp;</span> three &amp;</p>")
let p : Element = try doc.select("p").first()!
let p: Element = try doc.select("p").first()!
let span: Element = try doc.select("span").first()!
XCTAssertEqual("two &", try span.text())
@ -67,7 +67,7 @@ class TextNodeTest: XCTestCase {
XCTAssertEqual("Hello <b>there</b>", TextUtil.stripNewlines(try div.html())) // not great that we get \n<b>there there... must correct
}
func testWithSupplementaryCharacter()throws{
func testWithSupplementaryCharacter()throws {
#if !os(Linux)
let doc: Document = try SwiftSoup.parse(String(Character(UnicodeScalar(135361)!)))
let t: TextNode = doc.body()!.textNodes()[0]
@ -77,12 +77,11 @@ class TextNodeTest: XCTestCase {
static var allTests = {
return [
("testBlank" , testBlank),
("testTextBean" , testTextBean),
("testSplitText" , testSplitText),
("testSplitAnEmbolden" , testSplitAnEmbolden),
("testWithSupplementaryCharacter" , testWithSupplementaryCharacter)
("testBlank", testBlank),
("testTextBean", testTextBean),
("testSplitText", testSplitText),
("testSplitAnEmbolden", testSplitAnEmbolden),
("testWithSupplementaryCharacter", testWithSupplementaryCharacter)
]
}()
}

View File

@ -10,7 +10,7 @@ import Foundation
@testable import SwiftSoup
class TextUtil {
public static func stripNewlines(_ text: String)->String {
public static func stripNewlines(_ text: String) -> String {
let regex = try! NCRegularExpression(pattern: "\\n\\s*", options: .caseInsensitive)
var str = text
str = regex.stringByReplacingMatches(in: str, options: [], range: NSRange(0..<str.utf16.count), withTemplate: "")

View File

@ -65,12 +65,12 @@ class TokenQueueTest: XCTestCase {
static var allTests = {
return [
("testChompBalanced" , testChompBalanced),
("testChompEscapedBalanced" , testChompEscapedBalanced),
("testChompBalancedMatchesAsMuchAsPossible" , testChompBalancedMatchesAsMuchAsPossible),
("testUnescape" , testUnescape),
("testChompToIgnoreCase" , testChompToIgnoreCase),
("testAddFirst" , testAddFirst)
("testChompBalanced", testChompBalanced),
("testChompEscapedBalanced", testChompEscapedBalanced),
("testChompBalancedMatchesAsMuchAsPossible", testChompBalancedMatchesAsMuchAsPossible),
("testUnescape", testUnescape),
("testChompToIgnoreCase", testChompToIgnoreCase),
("testAddFirst", testAddFirst)
]
}()

View File

@ -15,7 +15,7 @@ class XmlTreeBuilderTest: XCTestCase {
let xml = "<doc id=2 href='/bar'>Foo <br /><link>One</link><link>Two</link></doc>"
let tb: XmlTreeBuilder = XmlTreeBuilder()
let doc: Document = try tb.parse(xml, "http://foo.com/")
XCTAssertEqual("<doc id=\"2\" href=\"/bar\">Foo <br /><link>One</link><link>Two</link></doc>",try TextUtil.stripNewlines(doc.html()))
XCTAssertEqual("<doc id=\"2\" href=\"/bar\">Foo <br /><link>One</link><link>Two</link></doc>", try TextUtil.stripNewlines(doc.html()))
XCTAssertEqual(try doc.getElementById("2")?.absUrl("href"), "http://foo.com/bar")
}
@ -24,20 +24,20 @@ class XmlTreeBuilderTest: XCTestCase {
let xml = "<doc><val>One<val>Two</val></bar>Three</doc>"
let tb: XmlTreeBuilder = XmlTreeBuilder()
let doc = try tb.parse(xml, "http://foo.com/")
XCTAssertEqual("<doc><val>One<val>Two</val>Three</val></doc>",try TextUtil.stripNewlines(doc.html()))
XCTAssertEqual("<doc><val>One<val>Two</val>Three</val></doc>", try TextUtil.stripNewlines(doc.html()))
}
func testCommentAndDocType()throws {
let xml = "<!DOCTYPE HTML><!-- a comment -->One <qux />Two"
let tb: XmlTreeBuilder = XmlTreeBuilder()
let doc = try tb.parse(xml, "http://foo.com/")
XCTAssertEqual("<!DOCTYPE HTML><!-- a comment -->One <qux />Two",try TextUtil.stripNewlines(doc.html()))
XCTAssertEqual("<!DOCTYPE HTML><!-- a comment -->One <qux />Two", try TextUtil.stripNewlines(doc.html()))
}
func testSupplyParserToJsoupClass()throws {
let xml = "<doc><val>One<val>Two</val></bar>Three</doc>"
let doc = try SwiftSoup.parse(xml, "http://foo.com/", Parser.xmlParser())
try XCTAssertEqual("<doc><val>One<val>Two</val>Three</val></doc>",TextUtil.stripNewlines(doc.html()))
try XCTAssertEqual("<doc><val>One<val>Two</val>Three</val></doc>", TextUtil.stripNewlines(doc.html()))
}
//TODO: nabil
@ -69,20 +69,19 @@ class XmlTreeBuilderTest: XCTestCase {
// TextUtil.stripNewlines(doc.html()));
// }
func testDoesNotForceSelfClosingKnownTags()throws {
// html will force "<br>one</br>" to logically "<br />One<br />". XML should be stay "<br>one</br> -- don't recognise tag.
let htmlDoc = try SwiftSoup.parse("<br>one</br>")
XCTAssertEqual("<br>one\n<br>", try htmlDoc.body()?.html())
let xmlDoc = try SwiftSoup.parse("<br>one</br>", "",Parser.xmlParser())
let xmlDoc = try SwiftSoup.parse("<br>one</br>", "", Parser.xmlParser())
XCTAssertEqual("<br>one</br>", try xmlDoc.html())
}
func testHandlesXmlDeclarationAsDeclaration()throws {
let html = "<?xml encoding='UTF-8' ?><body>One</body><!-- comment -->"
let doc = try SwiftSoup.parse(html, "", Parser.xmlParser())
try XCTAssertEqual("<?xml encoding=\"UTF-8\"?> <body> One </body> <!-- comment -->",StringUtil.normaliseWhitespace(doc.outerHtml()))
try XCTAssertEqual("<?xml encoding=\"UTF-8\"?> <body> One </body> <!-- comment -->", StringUtil.normaliseWhitespace(doc.outerHtml()))
XCTAssertEqual("#declaration", doc.childNode(0).nodeName())
XCTAssertEqual("#comment", doc.childNode(2).nodeName())
}
@ -159,21 +158,21 @@ class XmlTreeBuilderTest: XCTestCase {
static var allTests = {
return [
("testSimpleXmlParse" , testSimpleXmlParse),
("testPopToClose" , testPopToClose),
("testCommentAndDocType" , testCommentAndDocType),
("testSupplyParserToJsoupClass" , testSupplyParserToJsoupClass),
("testDoesNotForceSelfClosingKnownTags" , testDoesNotForceSelfClosingKnownTags),
("testSimpleXmlParse", testSimpleXmlParse),
("testPopToClose", testPopToClose),
("testCommentAndDocType", testCommentAndDocType),
("testSupplyParserToJsoupClass", testSupplyParserToJsoupClass),
("testDoesNotForceSelfClosingKnownTags", testDoesNotForceSelfClosingKnownTags),
("testHandlesXmlDeclarationAsDeclaration" , testHandlesXmlDeclarationAsDeclaration),
("testXmlFragment" , testXmlFragment),
("testXmlParseDefaultsToHtmlOutputSyntax" , testXmlParseDefaultsToHtmlOutputSyntax),
("testDoesHandleEOFInTag" , testDoesHandleEOFInTag),
("testParseDeclarationAttributes" , testParseDeclarationAttributes),
("testCaseSensitiveDeclaration" , testCaseSensitiveDeclaration),
("testCreatesValidProlog" , testCreatesValidProlog),
("preservesCaseByDefault" , preservesCaseByDefault),
("canNormalizeCase" , canNormalizeCase)
("testHandlesXmlDeclarationAsDeclaration", testHandlesXmlDeclarationAsDeclaration),
("testXmlFragment", testXmlFragment),
("testXmlParseDefaultsToHtmlOutputSyntax", testXmlParseDefaultsToHtmlOutputSyntax),
("testDoesHandleEOFInTag", testDoesHandleEOFInTag),
("testParseDeclarationAttributes", testParseDeclarationAttributes),
("testCaseSensitiveDeclaration", testCaseSensitiveDeclaration),
("testCreatesValidProlog", testCreatesValidProlog),
("preservesCaseByDefault", preservesCaseByDefault),
("canNormalizeCase", canNormalizeCase)
]
}()