use crate::item::{Node, NodeType};
use crate::qname::QualifiedName;
use crate::trees::smite::RNode;
use crate::validators::relaxng::pattern::Param;
use crate::value::Value;
use std::collections::HashMap;
use std::rc::Rc;
pub(crate) fn derive(doc: &RNode, pat: RNode, refs: &HashMap<String, RNode>) -> RNode {
child_deriv(pat, doc.child_iter().next().unwrap(), refs)
}
pub(crate) fn is_nullable(pat: RNode) -> bool {
match pat.name().localname_to_string().as_str() {
"empty" => true,
"text" => true,
"group" => {
let mut pc = pat.child_iter();
let p1 = pc.next().unwrap();
let p2 = pc.next().unwrap();
is_nullable(p1) && is_nullable(p2)
}
"interleave" => {
let mut pc = pat.child_iter();
let p1 = pc.next().unwrap();
let p2 = pc.next().unwrap();
is_nullable(p1) && is_nullable(p2)
}
"choice" => {
let mut pc = pat.child_iter();
let p1 = pc.next().unwrap();
let p2 = pc.next().unwrap();
is_nullable(p1) || is_nullable(p2)
}
"oneOrMore" => is_nullable(pat.first_child().unwrap()),
_ => false,
}
}
fn contains(nc: RNode, qn: Rc<QualifiedName>) -> bool {
match nc.name().localname_to_string().as_str() {
"anyName" => true,
"anyNameExcept" => {
let name = nc.first_child().unwrap();
!contains(name, qn)
}
"NSName" => {
let nsuri = nc.first_child().unwrap();
Some(nsuri.to_string()) == qn.namespace_uri_to_string()
}
"NSNameExcept" => {
let mut c = nc.child_iter();
let ns1 = c.next().unwrap();
let n = c.next().unwrap();
(Some(ns1.to_string()) == qn.namespace_uri_to_string()) && !contains(n, qn)
}
"name" => {
let ln1 = nc.first_child().unwrap();
let ns1 = nc.get_attribute(&QualifiedName::new(None, None, "ns"));
if ns1.to_string().is_empty() {
qn.namespace_uri().is_none() && (ln1.to_string() == qn.localname_to_string())
} else {
(Some(ns1.to_string()) == qn.namespace_uri_to_string()) && (ln1.to_string() == qn.localname_to_string())
}
}
"NameClassChoice" => {
let mut c = nc.child_iter();
let nc1 = c.next().unwrap();
let nc2 = c.next().unwrap();
contains(nc1, qn.clone()) || contains(nc2, qn)
}
_ => false,
}
}
fn child_deriv(pat: RNode, cn: RNode, refs: &HashMap<String, RNode>) -> RNode {
match cn.node_type() {
NodeType::Document
| NodeType::Attribute
| NodeType::Comment
| NodeType::ProcessingInstruction
| NodeType::Reference
| NodeType::Unknown
| NodeType::Namespace => pat
.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "notAllowed")))
.unwrap(),
NodeType::Text => text_deriv(pat, cn.value().to_string()),
NodeType::Element => {
let mut pat1 = start_tag_open_deriv(pat, cn.name(), refs);
for attribute in cn.attribute_iter() {
pat1 = att_deriv(attribute, pat1);
}
pat1 = start_tag_close_deriv(pat1);
pat1 = children_deriv(pat1, cn.clone(), refs);
pat1 = end_tag_deriv(pat1);
pat1
}
}
}
fn start_tag_open_deriv(pat: RNode, q: Rc<QualifiedName>, refs: &HashMap<String, RNode>) -> RNode {
match pat.name().localname_to_string().as_str() {
"ref" => {
let patname = pat.get_attribute(&QualifiedName::new(None, None, "name"));
let newpat = refs.get(patname.to_string().as_str());
match newpat {
None => pat
.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "notAllowed")))
.unwrap(),
Some(rn) => start_tag_open_deriv(rn.clone(), q, refs),
}
}
"element" => {
let mut pc = pat.child_iter();
let nc = pc.next().unwrap();
let p = pc.next().unwrap();
if contains(nc, q) {
after(
p,
pat.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "empty")))
.unwrap(),
)
} else {
pat.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "notAllowed".to_string())))
.unwrap()
}
}
"choice" => {
let mut pc = pat.child_iter();
choice(
start_tag_open_deriv(pc.next().unwrap(), q.clone(), refs),
start_tag_open_deriv(pc.next().unwrap(), q.clone(), refs),
)
}
"interleave" => {
let mut pc = pat.child_iter();
let p1 = pc.next().unwrap();
let p2 = pc.next().unwrap();
choice(
apply_after(
|pat: RNode| {
let mut i = pat
.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "interleave")))
.unwrap();
let _ = i.push(pat);
let _ = i.push(p2.clone());
i
},
start_tag_open_deriv(p1.clone(), q.clone(), refs),
),
apply_after(
|pat: RNode| {
let mut i = pat
.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "interleave")))
.unwrap();
let _ = i.push(pat);
let _ = i.push(p1.clone());
i
},
start_tag_open_deriv(p2.clone(), q.clone(), refs),
),
)
}
"oneOrMore" => {
let p1 = pat.first_child().unwrap();
apply_after(
|pat: RNode| {
group(
pat.clone(),
choice(
pat.clone(),
pat.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "empty")))
.unwrap(),
),
)
},
start_tag_open_deriv(p1, q, refs),
)
}
"group" => {
let mut pc = pat.child_iter();
let p1 = pc.next().unwrap();
let p2 = pc.next().unwrap();
let x = apply_after(
|pat: RNode| group(pat, p2.clone()),
start_tag_open_deriv(p1.clone(), q.clone(), refs),
);
if is_nullable(p1) {
choice(x, start_tag_open_deriv(p2, q, refs))
} else {
x
}
}
"after" => {
let mut pc = pat.child_iter();
let p1 = pc.next().unwrap();
let p2 = pc.next().unwrap();
apply_after(
|pat: RNode| after(pat, p2.clone()),
start_tag_open_deriv(p1, q, refs),
)
}
_ => pat
.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "notAllowed")))
.unwrap(),
}
}
fn att_deriv(pat: RNode, att: RNode) -> RNode {
match pat.name().localname_to_string().as_str() {
"after" => {
let mut pc = pat.child_iter();
let p1 = pc.next().unwrap();
let p2 = pc.next().unwrap();
after(att_deriv(p1, att), p2)
}
"choice" => {
let mut pc = pat.child_iter();
let p1 = pc.next().unwrap();
let p2 = pc.next().unwrap();
choice(att_deriv(p1, att.clone()), att_deriv(p2, att))
}
"group" => {
let mut pc = pat.child_iter();
let p1 = pc.next().unwrap();
let p2 = pc.next().unwrap();
choice(
group(att_deriv(p1.clone(), att.clone()), p2.clone()),
group(att_deriv(p2, att), p1),
)
}
"interleave" => {
let mut pc = pat.child_iter();
let p1 = pc.next().unwrap();
let p2 = pc.next().unwrap();
choice(
interleave(att_deriv(p1.clone(), att.clone()), p2.clone()),
interleave(att_deriv(p2, att), p1),
)
}
"oneOrMore" => {
let p1 = pat.first_child().unwrap();
group(
att_deriv(p1, att),
choice(
pat.clone(),
pat.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "empty")))
.unwrap(),
),
)
}
"attribute" => {
let (qn, av) = (att.name(), att.value());
let mut i = pat.child_iter();
let nc = i.next().unwrap();
let p1 = i.next().unwrap();
if contains(nc, qn) && value_match(p1, av.to_string()) {
pat.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "empty")))
.unwrap()
} else {
pat.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "notAllowed")))
.unwrap()
}
}
_ => pat
.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "notAllowed")))
.unwrap(),
}
}
fn start_tag_close_deriv(pat: RNode) -> RNode {
match pat.name().localname_to_string().as_str() {
"after" => {
let mut pc = pat.child_iter();
let p1 = pc.next().unwrap();
let p2 = pc.next().unwrap();
after(start_tag_close_deriv(p1), p2)
}
"choice" => {
let mut pc = pat.child_iter();
let p1 = pc.next().unwrap();
let p2 = pc.next().unwrap();
choice(start_tag_close_deriv(p1), start_tag_close_deriv(p2))
}
"group" => {
let mut pc = pat.child_iter();
let p1 = pc.next().unwrap();
let p2 = pc.next().unwrap();
group(start_tag_close_deriv(p1), start_tag_close_deriv(p2))
}
"interleave" => {
let mut pc = pat.child_iter();
let p1 = pc.next().unwrap();
let p2 = pc.next().unwrap();
interleave(start_tag_close_deriv(p1), start_tag_close_deriv(p2))
}
"oneOrMore" => {
let p = pat.first_child().unwrap();
one_or_more(start_tag_close_deriv(p))
}
"attribute" => pat
.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "notAllowed")))
.unwrap(),
_ => pat,
}
}
fn children_deriv(pat: RNode, cn: RNode, refs: &HashMap<String, RNode>) -> RNode {
match cn.clone().child_iter().count() {
0 => {
choice(pat.clone(), text_deriv(pat, "".to_string()))
}
1 => {
let n = cn.first_child().unwrap();
match n.node_type() {
NodeType::Text => {
let p1 = child_deriv(n.clone(), pat.clone(), refs);
if whitespace(n.value().to_string()) {
choice(pat, p1)
} else {
p1
}
}
_ => strip_children_deriv(pat, cn.child_iter(), refs),
}
}
_ => strip_children_deriv(pat, cn.child_iter(), refs),
}
}
fn end_tag_deriv(pat: RNode) -> RNode {
match pat.name().localname_to_string().as_str() {
"choice" => {
let mut pc = pat.child_iter();
let p1 = pc.next().unwrap();
let p2 = pc.next().unwrap();
choice(end_tag_deriv(p1), end_tag_deriv(p2))
}
"after" => {
let mut pc = pat.child_iter();
let p1 = pc.next().unwrap();
let p2 = pc.next().unwrap();
if is_nullable(p1) {
p2
} else {
pat.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "notAllowed")))
.unwrap()
}
}
_ => pat
.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "notAllowed")))
.unwrap(),
}
}
fn text_deriv(pat: RNode, s: String) -> RNode {
match pat.name().localname_to_string().as_str() {
"choice" => {
let mut pc = pat.child_iter();
let p1 = pc.next().unwrap();
let p2 = pc.next().unwrap();
choice(text_deriv(p1, s.clone()), text_deriv(p2, s.clone()))
}
"interleave" => {
let mut pc = pat.child_iter();
let p1 = pc.next().unwrap();
let p2 = pc.next().unwrap();
choice(
interleave(text_deriv(p1.clone(), s.clone()), p2.clone()),
interleave(p1, text_deriv(p2, s.clone())),
)
}
"group" => {
let mut pc = pat.child_iter();
let p1 = pc.next().unwrap();
let p2 = pc.next().unwrap();
let p = group(text_deriv(p1, s.clone()), p2.clone());
if is_nullable(p.clone()) {
choice(p.clone(), text_deriv(p2, s))
} else {
p
}
}
"after" => {
let mut pc = pat.child_iter();
let p1 = pc.next().unwrap();
let p2 = pc.next().unwrap();
after(text_deriv(p1, s), p2)
}
"oneOrMore" => {
let p = pat.first_child().unwrap();
group(
text_deriv(p.clone(), s.clone()),
choice(
pat.clone(),
pat.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "empty")))
.unwrap(),
),
)
}
"Text" => pat,
"Value" => {
let dtlib = pat.get_attribute(&QualifiedName::new(
None,
None,
"datatypeLibrary",
));
let dtname = pat.get_attribute(&QualifiedName::new(None, None, "type"));
let v = pat.value().to_string();
if datatype_equal((dtlib, dtname), v, s) {
pat.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "empty")))
.unwrap()
} else {
pat.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "notAllowed")))
.unwrap()
}
}
"Data" => {
let mut c = pat.child_iter();
let dt = c.next().unwrap();
let params = vec![];
if data_type_allows(dt, params, s) {
pat.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "empty")))
.unwrap()
} else {
pat.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "notAllowed")))
.unwrap()
}
}
"DataExcept" => {
let mut c = pat.clone().child_iter();
let dt = c.next().unwrap();
let params = vec![];
if data_type_allows(dt, params, s.clone()) && !is_nullable(text_deriv(pat.clone(), s)) {
pat.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "empty")))
.unwrap()
} else {
pat.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "notAllowed")))
.unwrap()
}
}
"List" => {
let p = pat.first_child().unwrap();
if is_nullable(list_deriv(p, stringsplit(s))) {
pat.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "empty")))
.unwrap()
} else {
pat.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "notAllowed")))
.unwrap()
}
}
_ => pat
.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "notAllowed")))
.unwrap(),
}
}
fn list_deriv(p: RNode, vs: Vec<String>) -> RNode {
let mut vsi = vs.into_iter();
match vsi.next() {
None => p,
Some(p1) => list_deriv(text_deriv(p, p1), vsi.collect()),
}
}
fn strip_children_deriv(
pat: RNode,
mut cn: Box<dyn Iterator<Item = RNode>>,
refs: &HashMap<String, RNode>,
) -> RNode {
match cn.next() {
None => pat,
Some(h) => strip_children_deriv(
if strip(h.clone()) {
pat
} else {
child_deriv(pat, h, refs)
},
cn,
refs,
),
}
}
pub fn apply_after<F1>(f: F1, pat: RNode) -> RNode
where
F1: Fn(RNode) -> RNode + Clone,
{
match pat.name().localname_to_string().as_str() {
"after" => {
let mut pc = pat.child_iter();
let p1 = pc.next().unwrap();
let p2 = pc.next().unwrap();
after(p1, f(p2))
}
"choice" => {
let mut pc = pat.child_iter();
let p1 = pc.next().unwrap();
let p2 = pc.next().unwrap();
choice(apply_after(f.clone(), p1), apply_after(f, p2))
}
"notAllowed" => pat,
_ => pat
.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "notAllowed")))
.unwrap(),
}
}
fn choice(pat1: RNode, pat2: RNode) -> RNode {
match (
pat1.name().localname_to_string().as_str(),
pat2.name().localname_to_string().as_str(),
) {
("notAllowed", _) => pat2,
(_, "notAllowed") => pat1,
(_, _) => {
let mut c = pat1
.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "choice")))
.unwrap();
let _ = c.push(pat1);
let _ = c.push(pat2);
c
}
}
}
fn group(pat1: RNode, pat2: RNode) -> RNode {
match (
pat1.name().localname_to_string().as_str(),
pat2.name().localname_to_string().as_str(),
) {
("notAllowed", _) => pat1,
(_, "notAllowed") => pat2,
("empty", _) => pat2,
(_, "empty") => pat1,
(_, _) => {
let mut g = pat1
.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "group")))
.unwrap();
let _ = g.push(pat1);
let _ = g.push(pat2);
g
}
}
}
fn after(pat1: RNode, pat2: RNode) -> RNode {
match (
pat1.name().localname_to_string().as_str(),
pat2.name().localname_to_string().as_str(),
) {
(_, "notAllowed") => pat2,
("notAllowed", _) => pat1,
(_, _) => {
let mut a = pat1
.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "after")))
.unwrap();
let _ = a.push(pat1);
let _ = a.push(pat2);
a
}
}
}
fn interleave(pat1: RNode, pat2: RNode) -> RNode {
match (
pat1.name().localname_to_string().as_str(),
pat2.name().localname_to_string().as_str(),
) {
("notAllowed", _) => pat1,
(_, "notAllowed") => pat2,
("empty", _) => pat2,
(_, "empty") => pat1,
(_, _) => {
let mut i = pat1
.owner_document()
.new_element(Rc::new(QualifiedName::new(None, None, "interleave")))
.unwrap();
let _ = i.push(pat1);
let _ = i.push(pat2);
i
}
}
}
fn value_match(pat: RNode, s: String) -> bool {
(is_nullable(pat.clone()) && whitespace(s.clone())) || is_nullable(text_deriv(pat, s))
}
fn whitespace(s: String) -> bool {
!s.contains(|c| !char::is_whitespace(c))
}
fn strip(c: RNode) -> bool {
match c.node_type() {
NodeType::Text => whitespace(c.value().to_string()),
_ => false,
}
}
fn one_or_more(pat: RNode) -> RNode {
match pat.name().localname_to_string().as_str() {
"notAllowed" => pat,
_ => {
let mut o = RNode::new_document()
.new_element(Rc::new(QualifiedName::new(None, None, "oneOrMore")))
.unwrap();
let _ = o.push(pat);
o
}
}
}
fn data_type_allows(dt: RNode, _params: Vec<Param>, _s: String) -> bool {
let _datatypens = dt.name().namespace_uri();
let datatype = dt.name().localname_to_string();
match datatype.as_str() {
"string" => true,
"token" => true,
_ => false,
}
}
fn datatype_equal((_d, s): (Rc<Value>, Rc<Value>), s1: String, s2: String) -> bool {
match s.as_ref() {
Value::String(_) => s1 == s2,
Value::Token => normalize_whitespace(s1) == normalize_whitespace(s2),
_ => false,
}
}
fn normalize_whitespace(s: String) -> String {
s.trim()
.split(' ')
.filter(|s| !s.is_empty())
.collect::<Vec<_>>()
.join(" ")
}
fn stringsplit(s: String) -> Vec<String> {
let t = s.split(' ').map(|u| u.to_string()).collect();
t
}