use crate::error::{ErrorLevel, StructuredError, ValidationErrorType};
use crate::node::{NodeType, XmlNode};
use crate::schema::types::{ContentModel, ElementDef, SimpleType, TypeDef};
use crate::schema::xsd::facets::{FacetConstraints, FacetValidator};
use super::DomSchemaValidator;
impl DomSchemaValidator {
pub(crate) fn collect_text_content(&self, node: &XmlNode) -> String {
let mut text = String::new();
for child in node.get_child_nodes() {
match child.get_type() {
NodeType::Text | NodeType::CData => {
if let Some(content) = child.get_content() {
text.push_str(&content);
}
}
_ => {}
}
}
text
}
pub(crate) fn validate_text_content(
&self,
node: &XmlNode,
elem: &ElementDef,
errors: &mut Vec<StructuredError>,
) {
let text_content = self.collect_text_content(node);
if text_content.is_empty() {
return;
}
let type_def = if let Some(ref type_ref) = elem.type_ref {
self.schema.get_type(type_ref).cloned()
} else {
elem.inline_type.clone()
};
match type_def {
Some(TypeDef::Simple(simple)) => {
self.validate_simple_type_facets(node, &simple, &text_content, errors);
}
Some(TypeDef::Complex(complex)) => {
if let ContentModel::SimpleContent { base_type } = &complex.content {
if let Some(TypeDef::Simple(simple)) = self.schema.get_type(base_type) {
self.validate_simple_type_facets(node, simple, &text_content, errors);
}
} else if !complex.mixed {
if let ContentModel::Sequence(_)
| ContentModel::Choice(_)
| ContentModel::All(_)
| ContentModel::ComplexExtension { .. } = &complex.content
{
let trimmed = text_content.trim();
if !trimmed.is_empty() {
let node_name = node.get_name();
let error = self
.make_error(
ValidationErrorType::InvalidContent,
format!(
"element '{}' has element-only content but contains text",
node_name
),
node,
)
.with_node_name(&node_name)
.with_level(ErrorLevel::Error);
if self.should_add_error(errors) {
errors.push(error);
}
}
}
}
}
None => {}
}
}
pub(crate) fn validate_simple_type_facets(
&self,
node: &XmlNode,
simple: &SimpleType,
text_content: &str,
errors: &mut Vec<StructuredError>,
) {
let constraints = self.create_facet_constraints(simple);
let validator = FacetValidator::new(&constraints);
if let Err(facet_error) = validator.validate(text_content) {
let node_name = node.get_name();
let error = self
.make_error(
ValidationErrorType::InvalidTextContent,
format!("element '{}': {}", node_name, facet_error),
node,
)
.with_node_name(&node_name)
.with_level(ErrorLevel::Error);
if self.should_add_error(errors) {
errors.push(error);
}
}
}
pub(crate) fn create_facet_constraints(&self, simple: &SimpleType) -> FacetConstraints {
let mut constraints = FacetConstraints::new();
if let Some(min_len) = simple.min_length {
constraints = constraints.with_min_length(min_len as usize);
}
if let Some(max_len) = simple.max_length {
constraints = constraints.with_max_length(max_len as usize);
}
if let Some(ref min_inc) = simple.min_inclusive {
constraints = constraints.with_min_inclusive(min_inc.clone());
}
if let Some(ref max_inc) = simple.max_inclusive {
constraints = constraints.with_max_inclusive(max_inc.clone());
}
if !simple.enumeration.is_empty() {
constraints = constraints.with_enumeration(simple.enumeration.clone());
}
if let Some(ref pattern) = simple.pattern {
constraints = constraints.with_pattern(pattern.clone());
}
constraints
}
pub(crate) fn make_error(
&self,
error_type: ValidationErrorType,
message: impl Into<String>,
node: &XmlNode,
) -> StructuredError {
let mut error = StructuredError::new(message, error_type);
if let Some(line) = node.line() {
error = error.with_line(line);
}
if let Some(column) = node.column() {
error = error.with_column(column);
}
error
}
pub(crate) fn should_add_error(&self, errors: &[StructuredError]) -> bool {
self.max_errors == 0 || errors.len() < self.max_errors
}
}