///|
/// Preserved XML content has string-valued attributes and its own namespace
/// scope. A known BPMN name inside this payload remains XML payload data.
pub(all) enum XmlContent {
Text(String)
Element(XmlElement)
Comment(String)
ProcessingInstruction(target~ : String, data~ : String)
} derive(Eq, Debug)
///|
pub extend XmlContent with Eq::{equal, not_equal}
///|
pub extend XmlContent with @debug.Debug::{to_repr}
///|
pub struct XmlElement { element : Element } derive(Eq, Debug)
///|
pub extend XmlElement with Eq::{equal, not_equal}
///|
pub extend XmlElement with @debug.Debug::{to_repr}
///|
pub fn XmlElement::new(name : ExpandedName, attributes? : ArrayView[(ExpandedName,String)] = [], contents? : ArrayView[XmlContent] = [], bindings? : ArrayView[(String,String)] = []) -> XmlElement {
let values=attributes.map(pair => Attribute::new(pair.0,Text(pair.1)))
let contents=contents.map(part => match part {
Text(s) => Content::Text(s)
Element(e) => Content::Element(e.element)
Comment(s) => Content::Comment(s)
ProcessingInstruction(target~,data~) => Content::ProcessingInstruction(target~,data~)
})
{ element: make_element(Kind::Foreign(name),values,contents,bindings,None,None,None) }
}
///|
pub fn XmlElement::from_element(element : Element) -> XmlElement? {
if opaque_kind(element.kind) { Some({ element, }) } else { None }
}
///|
pub fn XmlElement::name(self : XmlElement) -> ExpandedName { self.element.kind.name() }
///|
pub fn XmlElement::contents(self : XmlElement) -> ReadOnlyArray[Content] { self.element.contents }
///|
pub fn XmlElement::attributes(self : XmlElement) -> ReadOnlyArray[Attribute] { self.element.values }
///|
pub fn XmlElement::bindings(self : XmlElement) -> ReadOnlyArray[(String,String)] { self.element.bindings }
///|
pub fn Element::xml(self : Element) -> XmlElement? { XmlElement::from_element(self) }
///|
fn opaque_kind(kind : Kind) -> Bool { match kind { Foreign(_) | Unknown(_) => true; _ => false } }
///|
fn namespace_context(element : Element, inherited : @namespaces.HashMap[String,String]) -> @namespaces.HashMap[String,String] {
if element.captured_context { element.context } else {
let mut result=inherited
for (prefix,uri) in element.bindings { if result.get(prefix) != Some(uri) { result=result.add(prefix,uri) } }
result
}
}
///|
fn namespace_uri(scope : @namespaces.HashMap[String,String], prefix : String) -> String? {
if prefix == "xml" { Some("http://www.w3.org/XML/1998/namespace") }
else if prefix == "" { Some(scope.get("").unwrap_or("")) }
else { scope.get(prefix) }
}
///|
fn opaque_attribute(element : Element, name : ExpandedName) -> Bool {
if opaque_kind(element.kind) { return true }
if name.namespace_uri == "http://www.w3.org/XML/1998/namespace" && ["space","lang","base","id"].contains(name.local_name) { return false }
if name.namespace_uri == "http://www.w3.org/2001/XMLSchema-instance" && name.local_name == "type" { return false }
if element.value_type is Some(t) && schema_type(t) is Some(t) {
return !t.fields().iter().any(f => f.kind == Attribute && f.name == Some(name))
}
true
}
///|
pub fn Element::opaque_attributes(self : Element) -> ReadOnlyArray[Attribute] {
ReadOnlyArray::from_array(self.values.iter().filter(a => a.name.map(n => opaque_attribute(self,n)).unwrap_or(true)).to_array())
}
///|
fn namespace_sensitive(element : Element) -> Bool {
opaque_kind(element.kind) || element.values.iter().any(a => a.name.map(n => opaque_attribute(element,n)).unwrap_or(false))
}
///|
/// Conservative QName-looking tokens. This never interprets or executes vendor
/// text; it only keeps namespace identities stable when comparing/writing it.
fn text_prefixes(text : String) -> Array[String] {
let prefixes : Array[String] = []
let mut i=0
while i < text.length() {
let (c,width)=codepoint(text,i)
if !name_start(c) { i+=width; continue }
let start=i; i+=width
while i < text.length() {
let (c,width)=codepoint(text,i)
if !name_char(c) && c != 58 { break }
i+=width
}
if qname(text[start:i].to_owned()) is Some((prefix,_)) && prefix != "" { prefixes.push(prefix) }
}
prefixes
}
///|
fn opaque_strings(element : Element) -> Array[String] {
let values : Array[String] = []
for a in element.values { if a.name.map(n => opaque_attribute(element,n)).unwrap_or(false) { values.push(a.value.token()) } }
if opaque_kind(element.kind) {
for content in element.contents { match content {
Text(s) | Comment(s) => values.push(s)
ProcessingInstruction(target~,data~) => { values.push(target); values.push(data) }
_ => ()
} }
}
values
}
///|
fn opaque_namespace_equal(element : Element, a : @namespaces.HashMap[String,String], b : @namespaces.HashMap[String,String]) -> Bool {
if namespace_sensitive(element) && namespace_uri(a,"") != namespace_uri(b,"") { return false }
for text in opaque_strings(element) {
for prefix in text_prefixes(text) { if namespace_uri(a,prefix) != namespace_uri(b,prefix) { return false } }
}
true
}
///|
pub fn XmlElement::namespace_binding(self : XmlElement, prefix : String) -> String? { self.element.namespace_binding(prefix) }
///|
pub fn Element::namespace_binding(self : Element, prefix : String) -> String? { namespace_uri(self.context,prefix) }