use std::collections::HashSet;
#[cfg(feature = "xsd11")]
use std::collections::HashMap;
#[cfg(feature = "xsd11")]
use crate::ids::AttributeKey;
use crate::ids::{ElementKey, NameId, TypeKey};
use crate::types::value::XmlValue;
use super::content::ContentValidatorState;
use super::info::{ContentProcessing, ContentType, SchemaValidity, TypeSource};
#[cfg(feature = "xsd11")]
#[derive(Debug, Clone)]
pub struct InheritedAttributeValue {
pub value: String,
pub attribute_key: Option<AttributeKey>,
}
#[derive(Debug, Clone)]
pub struct ElementValidationState {
pub local_name: NameId,
pub namespace: Option<NameId>,
pub element_decl: Option<ElementKey>,
pub schema_type: Option<TypeKey>,
pub content_state: ContentValidatorState,
pub content_type: Option<ContentType>,
pub is_nil: bool,
pub is_default: bool,
pub member_type: Option<TypeKey>,
pub typed_value: Option<XmlValue>,
pub normalized_value: Option<String>,
pub validity: SchemaValidity,
pub error_codes: Vec<&'static str>,
pub any_child_not_full: bool,
pub any_child_not_none: bool,
pub any_attr_not_full: bool,
pub any_attr_not_none: bool,
pub strictly_assessed: bool,
pub notation: Option<crate::ids::NotationKey>,
pub ns_context: Option<crate::namespace::context::NamespaceContextSnapshot>,
pub element_serial: u64,
pub process_contents: ContentProcessing,
pub base_uri: String,
pub base_uri_set_by_xml_base: bool,
pub schema_location_hint_start: usize,
pub no_namespace_schema_location_hint_start: usize,
pub seen_attributes: HashSet<(Option<NameId>, NameId)>,
pub seen_id_attr: bool,
pub text_content: String,
pub has_text: bool,
pub has_element_children: bool,
pub type_source: Option<TypeSource>,
#[cfg(feature = "xsd11")]
pub cta_selected: bool,
#[cfg(feature = "xsd11")]
pub owns_assertion_buffer: bool,
#[cfg(feature = "xsd11")]
pub has_type_alternatives: bool,
#[cfg(feature = "xsd11")]
pub collected_attributes: Vec<(Option<NameId>, NameId, String)>,
#[cfg(feature = "xsd11")]
pub assertion_element_ref: Option<u32>,
#[cfg(feature = "xsd11")]
pub incoming_inherited: HashMap<(Option<NameId>, NameId), InheritedAttributeValue>,
#[cfg(feature = "xsd11")]
pub outgoing_inherited: HashMap<(Option<NameId>, NameId), InheritedAttributeValue>,
}
impl ElementValidationState {
pub fn new(local_name: NameId, namespace: Option<NameId>) -> Self {
ElementValidationState {
local_name,
namespace,
element_decl: None,
schema_type: None,
content_state: ContentValidatorState::Empty,
content_type: None,
is_nil: false,
is_default: false,
member_type: None,
typed_value: None,
normalized_value: None,
validity: SchemaValidity::NotKnown,
error_codes: Vec::new(),
any_child_not_full: false,
any_child_not_none: false,
any_attr_not_full: false,
any_attr_not_none: false,
strictly_assessed: false,
notation: None,
ns_context: None,
element_serial: 0,
process_contents: ContentProcessing::Strict,
base_uri: String::new(),
base_uri_set_by_xml_base: false,
schema_location_hint_start: 0,
no_namespace_schema_location_hint_start: 0,
seen_attributes: HashSet::new(),
seen_id_attr: false,
text_content: String::new(),
has_text: false,
has_element_children: false,
type_source: None,
#[cfg(feature = "xsd11")]
cta_selected: false,
#[cfg(feature = "xsd11")]
owns_assertion_buffer: false,
#[cfg(feature = "xsd11")]
has_type_alternatives: false,
#[cfg(feature = "xsd11")]
collected_attributes: Vec::new(),
#[cfg(feature = "xsd11")]
assertion_element_ref: None,
#[cfg(feature = "xsd11")]
incoming_inherited: HashMap::new(),
#[cfg(feature = "xsd11")]
outgoing_inherited: HashMap::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ValidatorState {
None,
Start,
Element,
Attribute,
EndOfAttributes,
Text,
Whitespace,
EndElement,
Finish,
}
impl ValidatorState {
pub fn can_start_element(&self) -> bool {
matches!(
self,
ValidatorState::None
| ValidatorState::Start
| ValidatorState::EndOfAttributes
| ValidatorState::Text
| ValidatorState::Whitespace
| ValidatorState::EndElement
)
}
pub fn can_validate_attribute(&self) -> bool {
matches!(self, ValidatorState::Element | ValidatorState::Attribute)
}
pub fn can_end_attributes(&self) -> bool {
matches!(self, ValidatorState::Element | ValidatorState::Attribute)
}
pub fn can_validate_text(&self) -> bool {
matches!(
self,
ValidatorState::EndOfAttributes
| ValidatorState::Text
| ValidatorState::Whitespace
| ValidatorState::EndElement
)
}
pub fn can_end_element(&self) -> bool {
matches!(
self,
ValidatorState::EndOfAttributes
| ValidatorState::Text
| ValidatorState::Whitespace
| ValidatorState::EndElement
)
}
pub fn can_finish(&self) -> bool {
matches!(self, ValidatorState::EndElement | ValidatorState::None)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_element_validation_state_defaults() {
let state = ElementValidationState::new(NameId(1), None);
assert_eq!(state.local_name, NameId(1));
assert!(state.namespace.is_none());
assert!(state.element_decl.is_none());
assert!(state.schema_type.is_none());
assert!(state.content_type.is_none());
assert!(!state.is_nil);
assert!(!state.is_default);
assert_eq!(state.validity, SchemaValidity::NotKnown);
assert_eq!(state.process_contents, ContentProcessing::Strict);
assert!(state.seen_attributes.is_empty());
assert!(state.text_content.is_empty());
assert!(!state.has_text);
assert!(!state.has_element_children);
assert!(state.type_source.is_none());
#[cfg(feature = "xsd11")]
assert!(!state.cta_selected);
}
#[test]
fn test_element_validation_state_with_namespace() {
let state = ElementValidationState::new(NameId(5), Some(NameId(10)));
assert_eq!(state.local_name, NameId(5));
assert_eq!(state.namespace, Some(NameId(10)));
}
#[test]
fn test_seen_attributes_dedup() {
let mut state = ElementValidationState::new(NameId(1), None);
let attr = (None, NameId(100));
assert!(state.seen_attributes.insert(attr));
assert!(!state.seen_attributes.insert(attr));
assert_eq!(state.seen_attributes.len(), 1);
}
#[test]
fn test_validator_state_transitions() {
assert!(ValidatorState::None.can_start_element());
assert!(!ValidatorState::None.can_validate_attribute());
assert!(ValidatorState::None.can_finish());
assert!(ValidatorState::Element.can_validate_attribute());
assert!(ValidatorState::Element.can_end_attributes());
assert!(!ValidatorState::Element.can_validate_text());
assert!(!ValidatorState::Element.can_end_element());
assert!(ValidatorState::Attribute.can_validate_attribute());
assert!(ValidatorState::Attribute.can_end_attributes());
assert!(ValidatorState::EndOfAttributes.can_validate_text());
assert!(ValidatorState::EndOfAttributes.can_start_element());
assert!(ValidatorState::EndOfAttributes.can_end_element());
assert!(ValidatorState::Text.can_validate_text());
assert!(ValidatorState::Text.can_start_element());
assert!(ValidatorState::Text.can_end_element());
assert!(ValidatorState::EndElement.can_start_element());
assert!(ValidatorState::EndElement.can_end_element());
assert!(ValidatorState::EndElement.can_finish());
assert!(!ValidatorState::Finish.can_start_element());
assert!(!ValidatorState::Finish.can_validate_attribute());
assert!(!ValidatorState::Finish.can_validate_text());
assert!(!ValidatorState::Finish.can_end_element());
assert!(!ValidatorState::Finish.can_finish());
}
}