mod content;
mod lookup;
mod occurrence;
use std::collections::HashMap;
use std::sync::Arc;
use crate::document::XmlDocument;
use crate::error::{ErrorLevel, Result, StructuredError, ValidationErrorType};
use crate::node::{NodeType, XmlNode};
use crate::schema::types::{CompiledSchema, FlattenedChildren};
use super::ValidationMode;
use super::streaming::ValidationOptions;
pub struct DomSchemaValidator {
pub(crate) schema: Arc<CompiledSchema>,
pub(crate) mode: ValidationMode,
pub(crate) options: ValidationOptions,
pub(crate) max_errors: usize,
}
impl DomSchemaValidator {
pub fn new(schema: Arc<CompiledSchema>) -> Self {
Self {
schema,
mode: ValidationMode::Strict,
options: ValidationOptions::default(),
max_errors: 0,
}
}
pub fn with_mode(mut self, mode: ValidationMode) -> Self {
self.mode = mode;
self
}
pub fn with_options(mut self, options: ValidationOptions) -> Self {
self.options = options;
self
}
pub fn with_max_errors(mut self, max: usize) -> Self {
self.max_errors = max;
self
}
pub fn validate(&self, doc: &XmlDocument) -> Result<Vec<StructuredError>> {
let mut errors = Vec::new();
if let Ok(root) = doc.get_root_element() {
self.validate_node_recursive(&root, None, &mut errors);
}
Ok(errors)
}
fn validate_node_recursive(
&self,
node: &XmlNode,
parent_allowed_children: Option<&FlattenedChildren>,
errors: &mut Vec<StructuredError>,
) {
if self.max_errors > 0 && errors.len() >= self.max_errors {
return;
}
match node.get_type() {
NodeType::Element => {
let allowed_children = self.validate_element(node, parent_allowed_children, errors);
for child in node.get_child_elements() {
self.validate_node_recursive(&child, allowed_children.as_deref(), errors);
}
}
NodeType::Document => {
for child in node.get_child_elements() {
self.validate_node_recursive(&child, None, errors);
}
}
_ => {
}
}
}
fn validate_element(
&self,
node: &XmlNode,
parent_allowed_children: Option<&FlattenedChildren>,
errors: &mut Vec<StructuredError>,
) -> Option<Arc<FlattenedChildren>> {
let name = node.get_name();
let prefix = node.get_prefix();
let elem_def = self.lookup_element(&name, prefix.as_deref());
let schema_has_elements = !self.schema.elements.is_empty();
let is_allowed_by_parent = parent_allowed_children
.map(|fc| fc.constraints.contains_key(&name))
.unwrap_or(false);
if let Some(elem) = elem_def {
let child_counts = self.count_child_elements(node);
let flattened = self.get_flattened_children_for_element(elem);
if let Some(ref fc) = flattened {
self.validate_min_occurs_batch(node, &child_counts, fc, errors);
self.validate_max_occurs_batch(node, &child_counts, fc, errors);
self.validate_sequence_order(node, fc, errors);
}
self.validate_text_content(node, elem, errors);
flattened
} else if is_allowed_by_parent {
None
} else if self.mode == ValidationMode::Strict && schema_has_elements {
let qname = match &prefix {
Some(p) => format!("{}:{}", p, name),
None => name.to_string(),
};
let error = self
.make_error(
ValidationErrorType::UnknownElement,
format!("element '{}' is not declared in schema", qname),
node,
)
.with_node_name(&qname)
.with_level(ErrorLevel::Error);
if self.should_add_error(errors) {
errors.push(error);
}
None
} else {
None
}
}
pub(crate) fn count_child_elements(&self, node: &XmlNode) -> HashMap<String, u32> {
let mut counts = HashMap::new();
for child in node.get_child_elements() {
let name = child.get_name();
*counts.entry(name).or_insert(0) += 1;
}
counts
}
}
#[cfg(test)]
mod tests;