///|
pub(all) struct XmlAttribute {
uri : String
name : String
value : String
} derive(Debug, Eq, ToJson)
///|
pub(all) struct XmlElement {
uri : String
name : String
attributes : Array[XmlAttribute]
content : Array[XmlContent]
} derive(Debug, Eq, ToJson)
///|
pub(all) enum XmlContent {
Text(String)
Element(XmlElement)
} derive(Debug, Eq, ToJson)
///|
priv struct XmlReader {
cs : Array[Char]
mut i : Int
mut nodes : Int
}
///|
fn xml_char(n : Int) -> Bool {
n == 9 ||
n == 10 ||
n == 13 ||
(n >= 32 && n <= 55295) ||
(n >= 57344 && n <= 65533) ||
(n >= 65536 && n <= 1114111)
}
///|
fn xml_text(s : String) -> Unit raise DavError {
if !s.iter().all(c => xml_char(c.to_int())) {
raise Invalid("invalid XML character")
}
}
///|
fn name_start(c : Char) -> Bool {
let n = c.to_int()
c == '_' ||
(c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
(n >= 192 && n <= 214) ||
(n >= 216 && n <= 246) ||
(n >= 248 && n <= 767) ||
(n >= 880 && n <= 893) ||
(n >= 895 && n <= 8191) ||
(n >= 8204 && n <= 8205) ||
(n >= 8304 && n <= 8591) ||
(n >= 11264 && n <= 12271) ||
(n >= 12289 && n <= 55295) ||
(n >= 63744 && n <= 64975) ||
(n >= 65008 && n <= 65533) ||
(n >= 65536 && n <= 983039)
}
///|
fn name_char(c : Char) -> Bool {
let n = c.to_int()
name_start(c) ||
c == '-' ||
c == '.' ||
(c >= '0' && c <= '9') ||
n == 183 ||
(n >= 768 && n <= 879) ||
(n >= 8255 && n <= 8256)
}
///|
fn ncname(s : String) -> Unit raise DavError {
let cs = s.to_array()
if cs.is_empty() || !name_start(cs[0]) || !cs.iter().all(name_char) {
raise Invalid("invalid XML local name")
}
}
///|
fn XmlReader::starts(self : XmlReader, text : String) -> Bool {
let cs = text.to_array()
if self.i + cs.length() > self.cs.length() {
return false
}
for j, c in cs {
if self.cs[self.i + j] != c {
return false
}
}
true
}
///|
fn XmlReader::space(self : XmlReader) -> Bool {
let start = self.i
while self.i < self.cs.length() &&
(
self.cs[self.i] == ' ' ||
self.cs[self.i] == '\n' ||
self.cs[self.i] == '\r' ||
self.cs[self.i] == '\t'
) {
self.i += 1
}
self.i != start
}
///|
fn XmlReader::need(self : XmlReader, text : String) -> Unit raise DavError {
if !self.starts(text) {
raise Invalid("malformed XML near offset " + self.i.to_string())
}
self.i += text.length()
}
///|
fn XmlReader::name(self : XmlReader) -> String raise DavError {
let out = StringBuilder()
if self.i >= self.cs.length() ||
!(name_start(self.cs[self.i]) || self.cs[self.i] == ':') {
raise Invalid("XML name")
}
while self.i < self.cs.length() &&
(name_char(self.cs[self.i]) || self.cs[self.i] == ':') {
out.write_char(self.cs[self.i])
self.i += 1
}
out.to_string()
}
///|
fn split_name(s : String) -> (String, String) raise DavError {
let parts = s.split(":").collect()
if parts.length() == 1 {
ncname(s)
return ("", s)
}
if parts.length() != 2 {
raise Invalid("invalid XML QName")
}
let prefix = parts[0].to_owned()
let local_name = parts[1].to_owned()
ncname(prefix)
ncname(local_name)
(prefix, local_name)
}
///|
fn XmlReader::until(self : XmlReader, stop : String) -> String raise DavError {
let out = StringBuilder()
while !self.starts(stop) {
if self.i >= self.cs.length() {
raise Invalid("unterminated XML construct")
}
out.write_char(self.cs[self.i])
self.i += 1
}
self.need(stop)
out.to_string()
}
///|
fn XmlReader::reference(self : XmlReader) -> Char raise DavError {
self.need("&")
let out = StringBuilder()
let mut count = 0
while !self.starts(";") {
if self.i >= self.cs.length() || count >= 16 {
raise Invalid("invalid XML entity")
}
out.write_char(self.cs[self.i])
self.i += 1
count += 1
}
self.need(";")
let value = out.to_string()
match value {
"amp" => '&'
"lt" => '<'
"gt" => '>'
"quot" => '"'
"apos" => '\''
_ => {
let cs = value.to_array()
if cs.length() < 2 || cs[0] != '#' {
raise Invalid("unknown XML entity")
}
let hex = cs[1] == 'x'
let start = if hex { 2 } else { 1 }
if start >= cs.length() {
raise Invalid("empty character reference")
}
let base = if hex { 16 } else { 10 }
let mut number = 0
for i in start..= '0' && c <= '9' {
c.to_int() - 48
} else if hex && c >= 'A' && c <= 'F' {
c.to_int() - 55
} else if hex && c >= 'a' && c <= 'f' {
c.to_int() - 87
} else {
-1
}
if digit < 0 || digit >= base || number > (1114111 - digit) / base {
raise Invalid("invalid character reference")
}
number = number * base + digit
}
if !xml_char(number) {
raise Invalid("forbidden XML character reference")
}
number.unsafe_to_char()
}
}
}
///|
fn XmlReader::attribute_value(
self : XmlReader,
declaration? : Bool = false,
) -> String raise DavError {
if self.i >= self.cs.length() {
raise Invalid("missing XML attribute value")
}
let quote = self.cs[self.i]
if quote != '"' && quote != '\'' {
raise Invalid("XML attribute quote")
}
self.i += 1
let out = StringBuilder()
while self.i < self.cs.length() && self.cs[self.i] != quote {
let c = self.cs[self.i]
if c == '<' || (declaration && c == '&') {
raise Invalid("invalid attribute or declaration value")
}
if c == '&' {
out.write_char(self.reference())
} else {
out.write_char(if c == '\t' || c == '\n' || c == '\r' { ' ' } else { c })
self.i += 1
}
}
self.need(quote.to_string())
out.to_string()
}
///|
fn XmlReader::comment(self : XmlReader) -> Unit raise DavError {
self.need("")
if text.contains("--") || text.has_suffix("-") {
raise Invalid("invalid XML comment")
}
}
///|
fn XmlReader::instruction(self : XmlReader) -> Unit raise DavError {
self.need("")
let target = self.name()
if target.to_lower() == "xml" {
raise Invalid("reserved XML instruction")
}
if !self.starts("?>") && !self.space() {
raise Invalid("invalid XML instruction")
}
ignore(self.until("?>"))
}
///|
fn XmlReader::misc(self : XmlReader) -> Unit raise DavError {
while true {
ignore(self.space())
if self.starts("