1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
//! Type children cache building for performance optimization.
use std::collections::HashSet;
use std::sync::Arc;
use crate::schema::types::{
CompiledSchema, ComplexType, ContentModel, ContentModelType, ElementDef, FlattenedChildren,
NsName, TypeDef,
};
use super::XsdCompiler;
impl XsdCompiler {
/// Builds the type children cache.
///
/// This pre-computes the flattened child element constraints for each complex type,
/// including elements inherited through type extension.
///
/// The primary cache is `ns_type_children_cache` keyed by (namespace_uri, local_name),
/// which is collision-free. The legacy `type_children_cache` (keyed by "prefix:local")
/// is also populated for backward compatibility.
pub(crate) fn build_type_children_cache(&self, schema: &mut CompiledSchema) {
// Collect type names first to avoid borrowing issues
let type_names: Vec<String> = schema.types.keys().cloned().collect();
// Build cache for main schema types
for type_name in &type_names {
if let Some(TypeDef::Complex(complex)) = schema.types.get(type_name) {
let flattened = Arc::new(self.flatten_type_children_ns(complex, schema));
// --- Namespace-aware cache (primary) ---
if let Some(ns_name) = self.resolve_to_ns(type_name) {
schema
.ns_type_children_cache
.insert(ns_name, Arc::clone(&flattened));
}
// --- Legacy prefix-based cache ---
schema
.type_children_cache
.insert(type_name.clone(), Arc::clone(&flattened));
// Also insert with local name for fallback lookup (first-wins).
let local_name = type_name
.split_once(':')
.map(|(_, local)| local)
.unwrap_or(type_name);
schema
.type_children_cache
.entry(local_name.to_string())
.or_insert(Arc::clone(&flattened));
}
}
// Build cache for imported schema types
let import_types: Vec<(String, FlattenedChildren)> = schema
.imports
.values()
.flat_map(|imported| {
imported.types.iter().filter_map(|(type_name, type_def)| {
if let TypeDef::Complex(complex) = type_def {
let flattened = self.flatten_type_children_ns(complex, schema);
Some((type_name.clone(), flattened))
} else {
None
}
})
})
.collect();
for (type_name, flattened) in import_types {
let flattened = Arc::new(flattened);
// --- Namespace-aware cache ---
if let Some(ns_name) = self.resolve_to_ns(&type_name) {
schema
.ns_type_children_cache
.insert(ns_name, Arc::clone(&flattened));
}
// --- Legacy prefix-based cache ---
schema
.type_children_cache
.insert(type_name.clone(), Arc::clone(&flattened));
let local_name = type_name
.split_once(':')
.map(|(_, local)| local)
.unwrap_or(&type_name);
schema
.type_children_cache
.entry(local_name.to_string())
.or_insert(Arc::clone(&flattened));
}
}
/// Resolves a prefixed or unprefixed type/base-type key to NsName using namespace_bindings.
fn resolve_to_ns(&self, key: &str) -> Option<NsName> {
if let Some((prefix, local)) = key.split_once(':') {
let ns_uri = self.namespace_bindings.get(prefix)?;
Some(NsName::new(ns_uri.clone(), local))
} else {
let ns = self.current_target_ns.as_deref().unwrap_or("").to_string();
Some(NsName::new(ns, key))
}
}
/// Flattens the child element constraints for a complex type.
/// Uses namespace-aware base type resolution.
fn flatten_type_children_ns(
&self,
complex: &ComplexType,
schema: &CompiledSchema,
) -> FlattenedChildren {
let mut visited = HashSet::new();
let elements = self.collect_elements_with_inheritance_ns(complex, schema, &mut visited);
let content_model_type = match &complex.content {
ContentModel::Sequence(_) => ContentModelType::Sequence,
ContentModel::Choice(_) => ContentModelType::Choice,
ContentModel::All(_) => ContentModelType::All,
ContentModel::ComplexExtension { .. } => ContentModelType::Sequence,
ContentModel::Empty => ContentModelType::Empty,
ContentModel::SimpleContent { .. } => ContentModelType::Empty,
ContentModel::Any { .. } => ContentModelType::Sequence,
};
let mut flattened = FlattenedChildren::with_content_model(content_model_type);
for elem in elements {
flattened
.constraints
.insert(elem.name.clone(), (elem.min_occurs, elem.max_occurs));
}
flattened
}
/// Collects all child elements from a complex type, including inherited elements.
/// Uses namespace-aware base type resolution to avoid cross-namespace collisions.
fn collect_elements_with_inheritance_ns(
&self,
complex: &ComplexType,
schema: &CompiledSchema,
visited: &mut HashSet<String>,
) -> Vec<ElementDef> {
let mut elements = Vec::new();
match &complex.content {
ContentModel::Sequence(elems)
| ContentModel::Choice(elems)
| ContentModel::All(elems) => {
elements.extend(elems.iter().cloned());
}
ContentModel::ComplexExtension {
base_type,
elements: ext_elements,
} => {
// First, get elements from the base type (inherited elements)
if !visited.contains(base_type.as_str()) {
visited.insert(base_type.clone());
// Resolve base type using namespace URI for correct cross-namespace lookup
let base_complex = if let Some(ns_name) = self.resolve_to_ns(base_type) {
schema.get_type_by_ns(&ns_name.namespace_uri, &ns_name.local_name)
} else {
None
};
// Fallback to legacy prefix-based lookup
let base_complex = base_complex.or_else(|| schema.get_type(base_type.as_str()));
if let Some(TypeDef::Complex(base_complex)) = base_complex {
let base_elements = self.collect_elements_with_inheritance_ns(
base_complex,
schema,
visited,
);
elements.extend(base_elements);
}
}
// Then add the extension's own elements
elements.extend(ext_elements.iter().cloned());
}
_ => {}
}
elements
}
}