141 lines
5.2 KiB
Swift
141 lines
5.2 KiB
Swift
//
|
|
// HTMLParser.swift
|
|
//
|
|
//
|
|
// Created by Isaac Paul on 7/31/24.
|
|
//
|
|
|
|
public class HTMLText : HTMLNode, IFlowContent {
|
|
|
|
public var content: String
|
|
|
|
public init(content: String) {
|
|
self.content = content
|
|
try! super.init( expectedAttributes: [:])
|
|
}
|
|
|
|
override public func toString(_ depth:Int = 0, spacingStrat:SpacingStrat = .tabs) -> (Int, String) {
|
|
return (depth, content)
|
|
}
|
|
}
|
|
|
|
public class XMLText : XMLNode {
|
|
|
|
public var content: String
|
|
|
|
public init(content: String) {
|
|
self.content = content
|
|
super.init()
|
|
}
|
|
}
|
|
|
|
extension XMLParser {
|
|
public func readObjects(endTag:String? = nil, validTags:[String]? = nil) throws -> [HTMLNode] {
|
|
var allItems:[HTMLNode] = []
|
|
while let obj = try self.readObject(endTag: endTag, validTags: validTags, xmlToHtmlMapper) {
|
|
allItems.append(obj)
|
|
}
|
|
return allItems
|
|
}
|
|
|
|
public func readObject(endTag:String? = nil, validTags:[String]? = nil, _ mapper:(_ name:String, _ attributes:[String:String], _ parser:XMLParser?) throws -> HTMLNode) throws -> HTMLNode? {
|
|
let tag = try readToken()
|
|
switch (tag) {
|
|
case let .data(unicode):
|
|
var result = ""
|
|
result.unicodeScalars.append(contentsOf: unicode)
|
|
return HTMLText(content: result)
|
|
case .instruction:
|
|
throw AppError("Unexpected Instruction")
|
|
case let .tag_empty(name, attributes):
|
|
if let validTags = validTags {
|
|
if (validTags.firstIndex(of: name) == nil) {
|
|
throw AppError("Invalid tag: \(name) for element \(endTag ?? "root")")
|
|
}
|
|
}
|
|
let mapped = try mapper(name, attributes, nil)
|
|
return mapped
|
|
case let .tag_end(name):
|
|
if let tag = endTag,
|
|
name != tag {
|
|
throw AppError("Unexpected end tag: \(name) for element \(tag)")
|
|
}
|
|
return nil
|
|
case let .tag_start(name, attributes):
|
|
if let validTags = validTags {
|
|
if (validTags.firstIndex(of: name) == nil) {
|
|
throw AppError("Invalid tag: \(name) for element \(endTag ?? "root")")
|
|
}
|
|
}
|
|
return try mapper(name, attributes, self)
|
|
case .none:
|
|
if (endTag == nil) {
|
|
return nil
|
|
} else {
|
|
throw AppError("Reached end of document before closing tag: \(endTag ?? "root")")
|
|
}
|
|
}
|
|
}
|
|
|
|
public func readObjectXml(endTag:String? = nil, validTags:[String]? = nil) throws -> XMLNode? {
|
|
let tag = try readToken()
|
|
switch (tag) {
|
|
case let .data(unicode):
|
|
var result = ""
|
|
result.unicodeScalars.append(contentsOf: unicode)
|
|
return XMLText(content: result)
|
|
case .instruction:
|
|
throw AppError("Unexpected Instruction")
|
|
case let .tag_empty(name, attributes):
|
|
if let validTags = validTags {
|
|
if (validTags.firstIndex(of: name) == nil) {
|
|
throw AppError("Invalid tag: \(name) for element \(endTag ?? "root")")
|
|
}
|
|
}
|
|
return try GenericXMLNode(name, attributes, nil)
|
|
case let .tag_end(name):
|
|
if let tag = endTag,
|
|
name != tag {
|
|
throw AppError("Unexpected end tag: \(name) for element \(tag)")
|
|
}
|
|
return nil
|
|
case let .tag_start(name, attributes):
|
|
if let validTags = validTags {
|
|
if (validTags.firstIndex(of: name) == nil) {
|
|
throw AppError("Invalid tag: \(name) for element \(endTag ?? "root")")
|
|
}
|
|
}
|
|
return try GenericXMLNode(name, attributes, self)
|
|
case .none:
|
|
if (endTag == nil) {
|
|
return nil
|
|
} else {
|
|
throw AppError("Reached end of document before closing tag: \(endTag ?? "root")")
|
|
}
|
|
}
|
|
}
|
|
|
|
public func readDataAsString(endTag:String) throws -> String? {
|
|
let tag = try readToken()
|
|
switch (tag) {
|
|
case let .data(unicode):
|
|
var result = ""
|
|
result.unicodeScalars.append(contentsOf: unicode)
|
|
return result
|
|
case .instruction:
|
|
throw AppError("Unexpected Instruction")
|
|
case let .tag_empty(name, attributes):
|
|
throw AppError("Unexpected empty tag: \(name)")
|
|
case let .tag_end(name):
|
|
if (name != endTag) {
|
|
throw AppError("Unexpected end tag: \(name)")
|
|
}
|
|
return nil
|
|
case let .tag_start(name, attributes):
|
|
throw AppError("Unexpected tag start: \(name)")
|
|
case .none:
|
|
throw AppError("Reached end of document before closing tag: \(endTag)")
|
|
}
|
|
}
|
|
}
|