1use crate::arenas::{ComplexTypeDefData, ModelGroupData};
7use crate::ids::ModelGroupKey;
8use crate::ids::{ElementKey, NameId, TypeKey};
9use crate::parser::frames::DerivationMethod;
10use crate::parser::frames::{
11 ComplexContentResult, Compositor, ElementFrameResult, ModelGroupDefResult, NamespaceToken,
12 NotQNameItem, OpenContentResult, ParticleResult, ParticleTerm, ProcessContents, QNameRef,
13 TypeRefResult, WildcardNamespace, WildcardResult,
14};
15use crate::parser::location::SourceRef;
16use crate::schema::model::{DefaultOpenContent, XsdVersion};
17use crate::schema::wildcard::{ElementWildcard, NamespaceConstraint as SchemaNamespaceConstraint};
18#[cfg(test)]
19use crate::schema::FormChoice;
20use crate::schema::SchemaSet;
21use crate::types::complex::{
22 NamespaceConstraint, OpenContent, OpenContentMode as TypesOpenContentMode,
23 ProcessContents as TypesProcessContents, WildcardRef,
24};
25
26use super::all_group::{
27 AllGroupModel, AllParticle, OpenContentMode as AllGroupOpenContentMode, OpenContentWildcard,
28};
29use super::error::{NfaCompileError, NfaCompileResult};
30use super::fragment::{fragment_to_table, FragmentBuilder, NfaFragment};
31use super::nfa::{NfaTable, NfaTerm};
32use super::particle::{apply_occurs, MaxOccurs};
33use super::ContentModelMatcher;
34
35const MAX_RECURSION_DEPTH: usize = 100;
37
38pub struct CompileContext<'a> {
42 pub schema_set: &'a SchemaSet,
44 pub target_namespace: Option<NameId>,
46 builder: FragmentBuilder,
48 depth: usize,
50 resolved_particle_types: Vec<Option<TypeKey>>,
52 current_particle_idx: Option<usize>,
54 content_flat_idx: Option<usize>,
57 resolved_particle_elements: Vec<Option<ElementKey>>,
59 current_sibling_elements: Vec<(Option<NameId>, NameId)>,
62 redefine_redirect: Option<(NameId, Option<NameId>, ModelGroupKey)>,
65 upa_mode: bool,
68}
69
70impl<'a> CompileContext<'a> {
71 pub fn new(schema_set: &'a SchemaSet, target_namespace: Option<NameId>) -> Self {
73 Self {
74 schema_set,
75 target_namespace,
76 builder: FragmentBuilder::new(),
77 depth: 0,
78 resolved_particle_types: Vec::new(),
79 current_particle_idx: None,
80 content_flat_idx: None,
81 resolved_particle_elements: Vec::new(),
82 current_sibling_elements: Vec::new(),
83 redefine_redirect: None,
84 upa_mode: false,
85 }
86 }
87
88 pub fn new_for_upa(schema_set: &'a SchemaSet, target_namespace: Option<NameId>) -> Self {
90 Self {
91 upa_mode: true,
92 ..Self::new(schema_set, target_namespace)
93 }
94 }
95
96 pub fn compile_particle(&mut self, particle: &ParticleResult) -> NfaCompileResult<NfaTable> {
100 self.check_recursion(particle.source.as_ref())?;
101 self.depth += 1;
102
103 let fragment = self.compile_particle_to_fragment(particle)?;
104 let table = fragment_to_table(fragment);
105
106 self.depth -= 1;
107 Ok(table)
108 }
109
110 pub fn compile_model_group(
114 &mut self,
115 group: &ModelGroupDefResult,
116 ) -> NfaCompileResult<NfaTable> {
117 self.check_recursion(group.source.as_ref())?;
118 self.depth += 1;
119
120 let fragment = self.compile_model_group_to_fragment(group)?;
121 let table = fragment_to_table(fragment);
122
123 self.depth -= 1;
124 Ok(table)
125 }
126
127 fn compile_particle_to_fragment(
129 &mut self,
130 particle: &ParticleResult,
131 ) -> NfaCompileResult<NfaFragment> {
132 if let Some(max) = particle.max_occurs {
134 if particle.min_occurs > max {
135 return Err(NfaCompileError::invalid_occurrence(
136 particle.min_occurs,
137 max,
138 particle.source.clone(),
139 ));
140 }
141 }
142
143 let term_fragment = self.compile_term(&particle.term, particle.source.as_ref())?;
145
146 let fragment =
148 self.apply_occurrences(term_fragment, particle.min_occurs, particle.max_occurs);
149
150 Ok(fragment)
151 }
152
153 fn compile_term(
155 &mut self,
156 term: &ParticleTerm,
157 source: Option<&SourceRef>,
158 ) -> NfaCompileResult<NfaFragment> {
159 match term {
160 ParticleTerm::Element(elem) => self.compile_element(elem, source),
161 ParticleTerm::Group(group) => self.compile_model_group_to_fragment(group),
162 ParticleTerm::Any(wildcard) => self.compile_wildcard(wildcard, source),
163 }
164 }
165
166 fn build_element_term(
172 &mut self,
173 elem: &ElementFrameResult,
174 source: Option<&SourceRef>,
175 ) -> NfaCompileResult<NfaTerm> {
176 let current_flat_idx = if let Some(flat_idx) = self.content_flat_idx {
178 self.content_flat_idx = Some(flat_idx + 1);
179 Some(flat_idx)
180 } else {
181 None
182 };
183
184 let (name, namespace, element_key) = if let Some(ref_name) = &elem.ref_name {
186 let key = self
193 .schema_set
194 .lookup_element(ref_name.namespace, ref_name.local_name);
195 if key.is_none() && ref_name.namespace.is_some() {
196 let name_str = crate::schema::resolver::format_resolved_qname(
197 &self.schema_set.name_table,
198 ref_name.namespace,
199 ref_name.local_name,
200 );
201 return Err(NfaCompileError::unresolved_element(
202 name_str,
203 elem.source.clone().or_else(|| source.cloned()),
204 ));
205 }
206 (ref_name.local_name, ref_name.namespace, key)
207 } else if let Some(name) = elem.name {
208 let source_ref = source.or(elem.source.as_ref());
210 let namespace = self.effective_element_namespace(elem, source_ref);
211 let local_key = current_flat_idx
213 .and_then(|idx| self.resolved_particle_elements.get(idx).copied().flatten())
214 .or_else(|| {
215 self.current_particle_idx
216 .and_then(|idx| self.resolved_particle_elements.get(idx).copied().flatten())
217 });
218 (name, namespace, local_key)
219 } else {
220 return Err(NfaCompileError::unresolved_element(
221 "anonymous element without name or ref".to_string(),
222 source.cloned(),
223 ));
224 };
225
226 let resolved_type = if element_key.is_none() {
228 let type_from_context = if let Some(flat_idx) = current_flat_idx {
230 self.resolved_particle_types
231 .get(flat_idx)
232 .copied()
233 .flatten()
234 } else {
235 self.current_particle_idx
236 .and_then(|idx| self.resolved_particle_types.get(idx).copied().flatten())
237 };
238 type_from_context.or_else(|| self.resolve_element_type_ref(elem))
240 } else {
241 None };
243
244 Ok(NfaTerm::element_with_type(
245 name,
246 namespace,
247 element_key,
248 resolved_type,
249 ))
250 }
251
252 fn compile_element(
254 &mut self,
255 elem: &ElementFrameResult,
256 source: Option<&SourceRef>,
257 ) -> NfaCompileResult<NfaFragment> {
258 let nfa_term = self.build_element_term(elem, source)?;
259 Ok(self.builder.single_term(nfa_term, source.cloned()))
260 }
261
262 fn resolve_element_type_ref(&self, elem: &ElementFrameResult) -> Option<TypeKey> {
264 match &elem.type_ref {
265 Some(TypeRefResult::QName(qname)) => self
266 .schema_set
267 .lookup_type(qname.namespace, qname.local_name)
268 .or_else(|| {
269 self.schema_set
270 .get_built_in_type_by_qname(qname.namespace, qname.local_name)
271 }),
272 _ => None, }
274 }
275
276 fn compile_wildcard(
278 &mut self,
279 wildcard: &WildcardResult,
280 source: Option<&SourceRef>,
281 ) -> NfaCompileResult<NfaFragment> {
282 let mut namespace_constraint = self.convert_wildcard_namespace(&wildcard.namespace);
283 let process_contents = self.convert_process_contents(wildcard.process_contents);
284
285 if let Some(not_ns) = self.convert_not_namespace(&wildcard.not_namespace) {
287 namespace_constraint = not_ns;
288 }
289
290 let not_qnames = self.expand_not_qnames(&wildcard.not_qname);
292
293 let nfa_term =
294 NfaTerm::wildcard_with_not_qnames(namespace_constraint, process_contents, not_qnames);
295 Ok(self.builder.single_term(nfa_term, source.cloned()))
296 }
297
298 fn expand_not_qnames(&self, items: &[NotQNameItem]) -> Vec<(Option<NameId>, NameId)> {
301 let mut result = Vec::new();
302 for item in items {
303 match item {
304 NotQNameItem::QName {
305 namespace,
306 local_name,
307 } => {
308 result.push((*namespace, *local_name));
309 }
310 NotQNameItem::Defined => {
311 result.extend(expand_defined_element_qnames(self.schema_set));
312 }
313 NotQNameItem::DefinedSibling => {
314 result.extend_from_slice(&self.current_sibling_elements);
315 }
316 }
317 }
318 result
319 }
320
321 fn compile_model_group_to_fragment(
323 &mut self,
324 group: &ModelGroupDefResult,
325 ) -> NfaCompileResult<NfaFragment> {
326 if let Some(ref_name) = &group.ref_name {
328 return self.compile_group_ref(ref_name, group.source.as_ref());
329 }
330
331 let compositor = group.compositor.unwrap_or(Compositor::Sequence);
333
334 if group.particles.is_empty() {
336 return Ok(self.builder.epsilon_fragment());
337 }
338
339 match compositor {
341 Compositor::Sequence => self.compile_sequence(&group.particles),
342 Compositor::Choice => self.compile_choice(&group.particles),
343 Compositor::All => self.compile_all(&group.particles, group.source.as_ref()),
344 }
345 }
346
347 fn compile_particle_with_index(
349 &mut self,
350 particle: &ParticleResult,
351 particle_idx: usize,
352 ) -> NfaCompileResult<NfaFragment> {
353 if self.content_flat_idx.is_some() {
354 return self.compile_particle_to_fragment(particle);
356 }
357 let saved_idx = self.current_particle_idx;
358 self.current_particle_idx = Some(particle_idx);
359 let result = self.compile_particle_to_fragment(particle);
360 self.current_particle_idx = saved_idx;
361 result
362 }
363
364 fn compile_sequence(&mut self, particles: &[ParticleResult]) -> NfaCompileResult<NfaFragment> {
366 if particles.is_empty() {
367 return Ok(self.builder.epsilon_fragment());
368 }
369
370 let new_siblings = self.collect_sibling_element_qnames(particles);
372 let saved_siblings = std::mem::replace(&mut self.current_sibling_elements, new_siblings);
373
374 let mut result = self.compile_particle_with_index(&particles[0], 0)?;
375 for (i, particle) in particles[1..].iter().enumerate() {
376 let frag = self.compile_particle_with_index(particle, i + 1)?;
377 result = result.concat(frag);
378 }
379
380 self.current_sibling_elements = saved_siblings;
381 Ok(result)
382 }
383
384 fn compile_choice(&mut self, particles: &[ParticleResult]) -> NfaCompileResult<NfaFragment> {
386 if particles.is_empty() {
387 return Ok(self.builder.epsilon_fragment());
388 }
389
390 let new_siblings = self.collect_sibling_element_qnames(particles);
392 let saved_siblings = std::mem::replace(&mut self.current_sibling_elements, new_siblings);
393
394 let mut result = self.compile_particle_with_index(&particles[0], 0)?;
395 for (i, particle) in particles[1..].iter().enumerate() {
396 let frag = self.compile_particle_with_index(particle, i + 1)?;
397 result = result.alternate(frag);
398 }
399
400 self.current_sibling_elements = saved_siblings;
401 Ok(result)
402 }
403
404 fn compile_all(
409 &mut self,
410 particles: &[ParticleResult],
411 source: Option<&SourceRef>,
412 ) -> NfaCompileResult<NfaFragment> {
413 for particle in particles {
415 if !matches!(particle.term, ParticleTerm::Element(_)) {
416 return Err(NfaCompileError::invalid_all_group(source.cloned()));
417 }
418
419 if let Some(max) = particle.max_occurs {
420 if max > 1 {
421 return Err(NfaCompileError::invalid_all_group(source.cloned()));
422 }
423 } else {
424 return Err(NfaCompileError::invalid_all_group(source.cloned()));
425 }
426 }
427
428 if particles.is_empty() {
429 return Ok(self.builder.epsilon_fragment());
430 }
431
432 let mut choice = self.compile_particle_with_index(&particles[0], 0)?;
436 for (i, particle) in particles[1..].iter().enumerate() {
437 let frag = self.compile_particle_with_index(particle, i + 1)?;
438 choice = choice.alternate(frag);
439 }
440
441 let n = particles.len() as u32;
442 Ok(choice.repeat_range(0, Some(n)))
443 }
444
445 fn compile_all_group_model(
453 &mut self,
454 particles: &[ParticleResult],
455 source: Option<&SourceRef>,
456 ) -> NfaCompileResult<AllGroupModel> {
457 let new_siblings = self.collect_sibling_element_qnames(particles);
459 let saved_siblings = std::mem::replace(&mut self.current_sibling_elements, new_siblings);
460
461 let mut all_particles = Vec::with_capacity(particles.len());
462
463 for particle in particles {
464 let term = match &particle.term {
465 ParticleTerm::Element(elem) => {
466 self.build_element_term(elem, particle.source.as_ref().or(source))?
467 }
468 ParticleTerm::Any(wildcard) => {
469 let mut ns = self.convert_wildcard_namespace(&wildcard.namespace);
470 let pc = self.convert_process_contents(wildcard.process_contents);
471 if let Some(not_ns) = self.convert_not_namespace(&wildcard.not_namespace) {
473 ns = not_ns;
474 }
475 let not_qnames = self.expand_not_qnames(&wildcard.not_qname);
476 NfaTerm::wildcard_with_not_qnames(ns, pc, not_qnames)
477 }
478 #[cfg(feature = "xsd11")]
479 ParticleTerm::Group(group) => {
480 if !self.schema_set.is_xsd11() {
482 return Err(NfaCompileError::invalid_all_group(
483 particle.source.clone().or_else(|| source.cloned()),
484 ));
485 }
486 if particle.min_occurs != 1 || particle.max_occurs != Some(1) {
488 return Err(NfaCompileError::InvalidAllGroupOccurs {
489 reason: "cos-all-limited.1.3: group reference inside xs:all \
490 must have minOccurs = maxOccurs = 1"
491 .into(),
492 location: particle.source.clone().or_else(|| source.cloned()),
493 });
494 }
495 if group.ref_name.is_none() {
497 return Err(NfaCompileError::invalid_all_group(
498 particle.source.clone().or_else(|| source.cloned()),
499 ));
500 }
501 self.flatten_all_group_ref_into(
503 group,
504 particle.source.as_ref().or(source),
505 &mut all_particles,
506 )?;
507 continue; }
509 #[cfg(not(feature = "xsd11"))]
510 ParticleTerm::Group(_) => {
511 return Err(NfaCompileError::invalid_all_group(source.cloned()));
512 }
513 };
514
515 let max_occurs = MaxOccurs::from_option(particle.max_occurs);
516 all_particles.push(AllParticle::new(
517 term,
518 particle.min_occurs,
519 max_occurs,
520 particle.source.clone().or_else(|| source.cloned()),
521 ));
522 }
523
524 self.current_sibling_elements = saved_siblings;
525 Ok(AllGroupModel::new(all_particles))
526 }
527
528 #[cfg(feature = "xsd11")]
539 fn flatten_all_group_ref_into(
540 &mut self,
541 group: &ModelGroupDefResult,
542 source: Option<&SourceRef>,
543 all_particles: &mut Vec<AllParticle>,
544 ) -> NfaCompileResult<()> {
545 self.check_recursion(source)?;
547 self.depth += 1;
548
549 let ref_name = group
550 .ref_name
551 .as_ref()
552 .expect("caller checked ref_name is Some");
553
554 let group_key = self.resolve_model_group_key(ref_name).ok_or_else(|| {
556 let name = format!(
557 "{}:{}",
558 ref_name
559 .namespace
560 .map(|n| format!("{:?}", n))
561 .unwrap_or_default(),
562 ref_name.local_name.0
563 );
564 NfaCompileError::unresolved_group(name, source.cloned())
565 })?;
566
567 let group_data = self
568 .schema_set
569 .arenas
570 .get_model_group(group_key)
571 .ok_or_else(|| {
572 NfaCompileError::unresolved_group(
573 format!("group key {:?}", group_key),
574 source.cloned(),
575 )
576 })?;
577
578 let compositor = group_data.compositor.unwrap_or(Compositor::Sequence);
580 if compositor != Compositor::All {
581 self.depth -= 1;
582 return Err(NfaCompileError::InvalidAllGroupContent {
583 location: source.cloned(),
584 });
585 }
586
587 let saved_flat_idx = self.content_flat_idx.take();
589 let saved_particle_elements = std::mem::take(&mut self.resolved_particle_elements);
590 let saved_types = std::mem::take(&mut self.resolved_particle_types);
591 let saved_idx = self.current_particle_idx;
592
593 self.resolved_particle_types = group_data.resolved_particle_types.clone();
594 self.resolved_particle_elements = group_data.resolved_particle_elements.clone();
595 self.content_flat_idx = Some(0);
596
597 let result = self.flatten_all_group_particles(&group_data.particles, source, all_particles);
599
600 self.content_flat_idx = saved_flat_idx;
602 self.resolved_particle_elements = saved_particle_elements;
603 self.resolved_particle_types = saved_types;
604 self.current_particle_idx = saved_idx;
605 self.depth -= 1;
606
607 result
608 }
609
610 #[cfg(feature = "xsd11")]
613 fn flatten_all_group_particles(
614 &mut self,
615 particles: &[ParticleResult],
616 source: Option<&SourceRef>,
617 all_particles: &mut Vec<AllParticle>,
618 ) -> NfaCompileResult<()> {
619 for particle in particles {
620 let term = match &particle.term {
621 ParticleTerm::Element(elem) => {
622 self.build_element_term(elem, particle.source.as_ref().or(source))?
623 }
624 ParticleTerm::Any(wildcard) => {
625 let mut ns = self.convert_wildcard_namespace(&wildcard.namespace);
626 let pc = self.convert_process_contents(wildcard.process_contents);
627 if let Some(not_ns) = self.convert_not_namespace(&wildcard.not_namespace) {
628 ns = not_ns;
629 }
630 let not_qnames = self.expand_not_qnames(&wildcard.not_qname);
631 NfaTerm::wildcard_with_not_qnames(ns, pc, not_qnames)
632 }
633 ParticleTerm::Group(inner_group) => {
634 if particle.min_occurs != 1 || particle.max_occurs != Some(1) {
637 return Err(NfaCompileError::InvalidAllGroupOccurs {
638 reason: "cos-all-limited.1.3: group reference inside xs:all \
639 must have minOccurs = maxOccurs = 1"
640 .into(),
641 location: particle.source.clone().or_else(|| source.cloned()),
642 });
643 }
644 if inner_group.ref_name.is_none() {
645 return Err(NfaCompileError::invalid_all_group(
646 particle.source.clone().or_else(|| source.cloned()),
647 ));
648 }
649 self.flatten_all_group_ref_into(
650 inner_group,
651 particle.source.as_ref().or(source),
652 all_particles,
653 )?;
654 continue;
655 }
656 };
657
658 let max_occurs = MaxOccurs::from_option(particle.max_occurs);
659 all_particles.push(AllParticle::new(
660 term,
661 particle.min_occurs,
662 max_occurs,
663 particle.source.clone().or_else(|| source.cloned()),
664 ));
665 }
666 Ok(())
667 }
668
669 fn compile_group_ref(
671 &mut self,
672 ref_name: &QNameRef,
673 source: Option<&SourceRef>,
674 ) -> NfaCompileResult<NfaFragment> {
675 self.check_recursion(source)?;
677 self.depth += 1;
678
679 let group_key = self.resolve_model_group_key(ref_name).ok_or_else(|| {
681 let name = format!(
682 "{}:{}",
683 ref_name
684 .namespace
685 .map(|n| format!("{:?}", n))
686 .unwrap_or_default(),
687 ref_name.local_name.0
688 );
689 NfaCompileError::unresolved_group(name, source.cloned())
690 })?;
691
692 let group_data = self
694 .schema_set
695 .arenas
696 .get_model_group(group_key)
697 .ok_or_else(|| {
698 NfaCompileError::unresolved_group(
699 format!("group key {:?}", group_key),
700 source.cloned(),
701 )
702 })?;
703
704 let result = self.compile_model_group_data(group_data, source);
706 self.depth -= 1;
707 result
708 }
709
710 fn resolve_model_group_key(&self, ref_name: &QNameRef) -> Option<ModelGroupKey> {
713 if let Some((name, ns, original_key)) = self.redefine_redirect {
714 if ref_name.local_name == name && ref_name.namespace == ns {
715 return Some(original_key);
716 }
717 }
718 self.schema_set
719 .lookup_model_group(ref_name.namespace, ref_name.local_name)
720 }
721
722 fn compile_model_group_data(
724 &mut self,
725 group: &ModelGroupData,
726 source: Option<&SourceRef>,
727 ) -> NfaCompileResult<NfaFragment> {
728 let compositor = group.compositor.unwrap_or(Compositor::Sequence);
729
730 if group.particles.is_empty() {
731 return Ok(self.builder.epsilon_fragment());
732 }
733
734 let saved_flat_idx = self.content_flat_idx.take();
736 let saved_particle_elements = std::mem::take(&mut self.resolved_particle_elements);
737 let saved_types = std::mem::take(&mut self.resolved_particle_types);
738 let saved_idx = self.current_particle_idx;
739
740 let saved_redirect = self.redefine_redirect.take();
742 if let (Some(original_key), Some(name)) = (group.redefine_original, group.name) {
743 self.redefine_redirect = Some((name, group.target_namespace, original_key));
744 }
745
746 self.resolved_particle_types = group.resolved_particle_types.clone();
748 self.resolved_particle_elements = group.resolved_particle_elements.clone();
749 self.content_flat_idx = Some(0);
751
752 let result = match compositor {
753 Compositor::Sequence => self.compile_sequence(&group.particles),
754 Compositor::Choice => self.compile_choice(&group.particles),
755 Compositor::All => self.compile_all(&group.particles, source),
756 };
757
758 self.redefine_redirect = saved_redirect;
760 self.content_flat_idx = saved_flat_idx;
761 self.resolved_particle_elements = saved_particle_elements;
762 self.resolved_particle_types = saved_types;
763 self.current_particle_idx = saved_idx;
764 result
765 }
766
767 fn apply_occurrences(
775 &mut self,
776 fragment: NfaFragment,
777 min: u32,
778 max: Option<u32>,
779 ) -> NfaFragment {
780 let (eff_min, eff_max) = if self.upa_mode {
781 cap_for_upa(min, max)
782 } else {
783 (min, max)
784 };
785 let max_occurs = MaxOccurs::from_option(eff_max);
786 apply_occurs(fragment, eff_min, max_occurs)
787 }
788
789 fn check_recursion(&self, source: Option<&SourceRef>) -> NfaCompileResult<()> {
791 if self.depth >= MAX_RECURSION_DEPTH {
792 return Err(NfaCompileError::recursion_exceeded(source.cloned()));
793 }
794 Ok(())
795 }
796
797 fn convert_wildcard_namespace(&self, ns: &WildcardNamespace) -> NamespaceConstraint {
799 match ns {
800 WildcardNamespace::Any => NamespaceConstraint::Any,
801 WildcardNamespace::Other => NamespaceConstraint::Other,
802 WildcardNamespace::TargetNamespace => NamespaceConstraint::TargetNamespace,
803 WildcardNamespace::Local => NamespaceConstraint::Local,
804 WildcardNamespace::List(list) => NamespaceConstraint::List(
805 list.iter()
806 .map(|t| t.resolve(self.target_namespace))
807 .collect(),
808 ),
809 }
810 }
811
812 fn convert_not_namespace(
815 &self,
816 not_namespace: &[NamespaceToken],
817 ) -> Option<NamespaceConstraint> {
818 if not_namespace.is_empty() {
819 return None;
820 }
821 let excluded: Vec<Option<NameId>> = not_namespace
822 .iter()
823 .map(|t| t.resolve(self.target_namespace))
824 .collect();
825 Some(NamespaceConstraint::Not(excluded))
826 }
827
828 fn convert_process_contents(&self, pc: ProcessContents) -> TypesProcessContents {
830 match pc {
831 ProcessContents::Strict => TypesProcessContents::Strict,
832 ProcessContents::Lax => TypesProcessContents::Lax,
833 ProcessContents::Skip => TypesProcessContents::Skip,
834 }
835 }
836
837 fn effective_element_namespace(
838 &self,
839 elem: &ElementFrameResult,
840 source: Option<&SourceRef>,
841 ) -> Option<NameId> {
842 self.schema_set.effective_local_element_namespace(
843 elem.target_namespace,
844 elem.form.as_deref(),
845 source,
846 self.target_namespace,
847 )
848 }
849
850 fn collect_sibling_element_qnames(
855 &self,
856 particles: &[ParticleResult],
857 ) -> Vec<(Option<NameId>, NameId)> {
858 self.collect_sibling_element_qnames_inner(particles, 0)
859 }
860
861 fn collect_sibling_element_qnames_inner(
862 &self,
863 particles: &[ParticleResult],
864 depth: usize,
865 ) -> Vec<(Option<NameId>, NameId)> {
866 if depth >= MAX_RECURSION_DEPTH {
868 return Vec::new();
869 }
870 let mut result = Vec::new();
871 for p in particles {
872 match &p.term {
873 ParticleTerm::Element(elem) => {
874 if let Some(ref_name) = &elem.ref_name {
875 result.push((ref_name.namespace, ref_name.local_name));
877 if let Some(head_key) = self
883 .schema_set
884 .lookup_element(ref_name.namespace, ref_name.local_name)
885 {
886 collect_substitution_members(self.schema_set, head_key, &mut result);
887 }
888 } else if let Some(name) = elem.name {
889 let source = p.source.as_ref().or(elem.source.as_ref());
891 let ns = self.effective_element_namespace(elem, source);
892 result.push((ns, name));
893 }
894 }
895 ParticleTerm::Group(group) => {
896 if let Some(ref_name) = &group.ref_name {
897 if let Some(key) = self.resolve_model_group_key(ref_name) {
898 if let Some(data) = self.schema_set.arenas.get_model_group(key) {
899 result.extend(self.collect_sibling_element_qnames_inner(
900 &data.particles,
901 depth + 1,
902 ));
903 }
904 }
905 }
906 }
907 ParticleTerm::Any(_) => {}
908 }
909 }
910 result
911 }
912}
913
914fn collect_substitution_members(
918 schema_set: &SchemaSet,
919 head_key: ElementKey,
920 out: &mut Vec<(Option<NameId>, NameId)>,
921) {
922 let head_key = schema_set
924 .arenas
925 .elements
926 .get(head_key)
927 .and_then(|e| e.resolved_ref)
928 .unwrap_or(head_key);
929 let mut visited = std::collections::HashSet::new();
930 let mut stack = vec![head_key];
931 while let Some(current) = stack.pop() {
932 if !visited.insert(current) {
933 continue;
934 }
935 for (member_key, member) in schema_set.arenas.elements.iter() {
936 if member_key == current {
937 continue;
938 }
939 if member.resolved_substitution_groups.contains(¤t) {
940 if let Some(name) = member.name {
941 let entry = (member.target_namespace, name);
942 if !out.contains(&entry) {
943 out.push(entry);
944 }
945 }
946 stack.push(member_key);
947 }
948 }
949 }
950}
951
952#[cfg_attr(test, allow(dead_code))]
961pub(super) fn cap_for_upa(min: u32, max: Option<u32>) -> (u32, Option<u32>) {
962 match (min, max) {
963 (0, Some(0)) => (0, Some(0)),
965 (m, Some(1)) if m <= 1 => (m, Some(1)),
967 (m, Some(mx)) if m == mx && m > 1 => (2, Some(2)),
969 (0, _) => (0, Some(2)),
971 (_, _) => (min.min(1), Some(2)),
973 }
974}
975
976pub fn compile_particle(
978 schema_set: &SchemaSet,
979 particle: &ParticleResult,
980 target_namespace: Option<NameId>,
981) -> NfaCompileResult<NfaTable> {
982 let mut ctx = CompileContext::new(schema_set, target_namespace);
983 ctx.compile_particle(particle)
984}
985
986pub fn compile_model_group(
988 schema_set: &SchemaSet,
989 group: &ModelGroupDefResult,
990 target_namespace: Option<NameId>,
991) -> NfaCompileResult<NfaTable> {
992 let mut ctx = CompileContext::new(schema_set, target_namespace);
993 ctx.compile_model_group(group)
994}
995
996pub(crate) fn is_top_level_all_group(
1002 particle: &ParticleResult,
1003) -> Option<(&[ParticleResult], Option<&SourceRef>)> {
1004 if let ParticleTerm::Group(group) = &particle.term {
1005 if group.compositor == Some(Compositor::All) && group.ref_name.is_none() {
1006 return Some((&group.particles, group.source.as_ref()));
1007 }
1008 }
1009 None
1010}
1011
1012pub(crate) fn resolve_top_level_all_group_ref<'a>(
1019 particle: &ParticleResult,
1020 schema_set: &'a SchemaSet,
1021) -> Option<&'a ModelGroupData> {
1022 if let ParticleTerm::Group(group) = &particle.term {
1023 let ref_name = group.ref_name.as_ref()?;
1024 let group_key = schema_set.lookup_model_group(ref_name.namespace, ref_name.local_name)?;
1025 let group_data = schema_set.arenas.get_model_group(group_key)?;
1026 if group_data.compositor == Some(Compositor::All) {
1027 return Some(group_data);
1028 }
1029 }
1030 None
1031}
1032
1033pub(crate) fn validate_outer_all_group_occurs(
1038 particle: &ParticleResult,
1039 xsd_version: XsdVersion,
1040) -> NfaCompileResult<()> {
1041 let min = particle.min_occurs;
1042 let max = particle.max_occurs; if xsd_version == XsdVersion::V1_0 {
1046 if min > 1 {
1047 return Err(NfaCompileError::InvalidAllGroupOccurs {
1048 reason: format!(
1049 "cos-all-limited.2: minOccurs must be 0 or 1 for xs:all group, found {}",
1050 min
1051 ),
1052 location: particle.source.clone(),
1053 });
1054 }
1055 match max {
1056 Some(1) => {} Some(n) => {
1058 return Err(NfaCompileError::InvalidAllGroupOccurs {
1059 reason: format!(
1060 "cos-all-limited.2: maxOccurs must be 1 for xs:all group, found {}",
1061 n
1062 ),
1063 location: particle.source.clone(),
1064 });
1065 }
1066 None => {
1067 return Err(NfaCompileError::InvalidAllGroupOccurs {
1068 reason: "cos-all-limited.2: maxOccurs='unbounded' not allowed for xs:all group"
1069 .to_string(),
1070 location: particle.source.clone(),
1071 });
1072 }
1073 }
1074 }
1075
1076 if let Some(max_val) = max {
1078 if min > max_val {
1079 return Err(NfaCompileError::InvalidOccurrence {
1080 min,
1081 max: max_val,
1082 location: particle.source.clone(),
1083 });
1084 }
1085 }
1086
1087 Ok(())
1088}
1089
1090#[cfg(feature = "xsd11")]
1095fn compile_base_all_group(
1096 schema_set: &SchemaSet,
1097 type_def: &ComplexTypeDefData,
1098) -> NfaCompileResult<Option<AllGroupModel>> {
1099 let base_ct_key = match type_def.resolved_base_type {
1100 Some(TypeKey::Complex(key)) => key,
1101 _ => return Ok(None),
1102 };
1103 let base_type_def = &schema_set.arenas.complex_types[base_ct_key];
1104 let base_matcher = compile_content_model_matcher(schema_set, base_type_def)?;
1105 match base_matcher {
1106 ContentModelMatcher::AllGroup(model) => Ok(Some(model)),
1107 _ => Ok(None),
1108 }
1109}
1110
1111#[cfg(feature = "xsd11")]
1115fn try_xsd10_empty_base_all_extension(
1116 ctx: &mut CompileContext<'_>,
1117 schema_set: &SchemaSet,
1118 type_def: &ComplexTypeDefData,
1119 is_extension: bool,
1120) -> NfaCompileResult<Option<ContentModelMatcher>> {
1121 if !is_extension || !schema_set.is_xsd10() {
1122 return Ok(None);
1123 }
1124 let Some(base_all_model) = compile_base_all_group(schema_set, type_def)? else {
1125 return Ok(None);
1126 };
1127 if !base_all_model.particles.is_empty() {
1128 return Ok(None);
1129 }
1130 let ComplexContentResult::Complex(def) = &type_def.content else {
1131 return Ok(None);
1132 };
1133 let Some(particle) = def.particle.as_ref() else {
1134 return Ok(None);
1135 };
1136 let Some(group_data) = resolve_top_level_all_group_ref(particle, schema_set) else {
1137 return Ok(None);
1138 };
1139 Ok(Some(compile_top_level_all_group_ref_matcher(
1140 ctx, schema_set, type_def, particle, group_data,
1141 )?))
1142}
1143
1144fn all_group_to_nfa(model: &AllGroupModel) -> NfaTable {
1148 let builder = FragmentBuilder::new();
1149 if model.particles.is_empty() {
1150 return fragment_to_table(builder.epsilon_fragment());
1151 }
1152
1153 let fragments: Vec<NfaFragment> = model
1154 .particles
1155 .iter()
1156 .map(|p| {
1157 let frag = builder.single_term(p.term.clone(), p.source.clone());
1158 let max = match p.max_occurs {
1159 MaxOccurs::Bounded(n) => Some(n),
1160 MaxOccurs::Unbounded => None,
1161 };
1162 apply_occurs(frag, p.min_occurs, MaxOccurs::from_option(max))
1163 })
1164 .collect();
1165
1166 let mut choice = fragments.into_iter().reduce(|a, b| a.alternate(b)).unwrap();
1167 let n = model.particles.len() as u32;
1168 choice = choice.repeat_range(0, Some(n));
1169
1170 fragment_to_table(choice)
1171}
1172
1173fn compile_top_level_all_group_ref_matcher(
1179 ctx: &mut CompileContext<'_>,
1180 schema_set: &SchemaSet,
1181 type_def: &ComplexTypeDefData,
1182 particle: &ParticleResult,
1183 group_data: &ModelGroupData,
1184) -> NfaCompileResult<ContentModelMatcher> {
1185 validate_outer_all_group_occurs(particle, schema_set.xsd_version)?;
1186 ctx.resolved_particle_types = group_data.resolved_particle_types.clone();
1187 ctx.resolved_particle_elements = group_data.resolved_particle_elements.clone();
1188 ctx.content_flat_idx = Some(0);
1189 if let (Some(original_key), Some(name)) = (group_data.redefine_original, group_data.name) {
1192 ctx.redefine_redirect = Some((name, group_data.target_namespace, original_key));
1193 }
1194 let mut model = ctx.compile_all_group_model(&group_data.particles, group_data.source.as_ref())?;
1195 if particle.min_occurs == 0 {
1196 model.outer_optional = true;
1197 }
1198 let base_matcher = ContentModelMatcher::AllGroup(model);
1199 let open_content = resolve_open_content(
1200 schema_set,
1201 &type_def.content,
1202 type_def.open_content.as_ref(),
1203 type_def.source.as_ref(),
1204 );
1205 Ok(attach_open_content(schema_set, base_matcher, open_content))
1206}
1207
1208pub fn compile_content_model_matcher(
1210 schema_set: &SchemaSet,
1211 type_def: &ComplexTypeDefData,
1212) -> NfaCompileResult<ContentModelMatcher> {
1213 compile_content_model_matcher_impl(schema_set, type_def, false)
1214}
1215
1216pub fn compile_content_model_for_upa(
1222 schema_set: &SchemaSet,
1223 type_def: &ComplexTypeDefData,
1224) -> NfaCompileResult<ContentModelMatcher> {
1225 compile_content_model_matcher_impl(schema_set, type_def, true)
1226}
1227
1228fn compile_content_model_matcher_impl(
1229 schema_set: &SchemaSet,
1230 type_def: &ComplexTypeDefData,
1231 upa_mode: bool,
1232) -> NfaCompileResult<ContentModelMatcher> {
1233 let target_namespace = type_def.target_namespace;
1234 let mut ctx = if upa_mode {
1235 CompileContext::new_for_upa(schema_set, target_namespace)
1236 } else {
1237 CompileContext::new(schema_set, target_namespace)
1238 };
1239 let is_extension = matches!(
1240 type_def.derivation_method,
1241 Some(DerivationMethod::Extension)
1242 );
1243
1244 if !is_extension {
1246 if let ComplexContentResult::Complex(def) = &type_def.content {
1247 if let Some(particle) = &def.particle {
1248 if let Some((all_particles, all_source)) = is_top_level_all_group(particle) {
1249 validate_outer_all_group_occurs(particle, schema_set.xsd_version)?;
1250 ctx.resolved_particle_types = type_def.resolved_content_particle_types.to_vec();
1251 ctx.resolved_particle_elements =
1252 type_def.resolved_content_particle_elements.to_vec();
1253 ctx.content_flat_idx = Some(0);
1254 let mut model = ctx.compile_all_group_model(all_particles, all_source)?;
1255 if particle.min_occurs == 0 {
1256 model.outer_optional = true;
1257 }
1258 let base_matcher = ContentModelMatcher::AllGroup(model);
1259
1260 let open_content = resolve_open_content(
1261 schema_set,
1262 &type_def.content,
1263 type_def.open_content.as_ref(),
1264 type_def.source.as_ref(),
1265 );
1266
1267 return Ok(attach_open_content(schema_set, base_matcher, open_content));
1268 }
1269
1270 if let Some(group_data) = resolve_top_level_all_group_ref(particle, schema_set) {
1272 return compile_top_level_all_group_ref_matcher(
1273 &mut ctx, schema_set, type_def, particle, group_data,
1274 );
1275 }
1276 }
1277 }
1278 }
1279
1280 #[cfg(feature = "xsd11")]
1286 if let Some(matcher) =
1287 try_xsd10_empty_base_all_extension(&mut ctx, schema_set, type_def, is_extension)?
1288 {
1289 return Ok(matcher);
1290 }
1291
1292 #[cfg(feature = "xsd11")]
1295 if is_extension && schema_set.is_xsd11() {
1296 if let Some(base_all_model) = compile_base_all_group(schema_set, type_def)? {
1297 let open_content = resolve_open_content(
1298 schema_set,
1299 &type_def.content,
1300 type_def.open_content.as_ref(),
1301 type_def.source.as_ref(),
1302 );
1303
1304 let own_particle = match &type_def.content {
1306 ComplexContentResult::Complex(def) => def.particle.as_ref(),
1307 _ => None,
1308 };
1309
1310 match own_particle {
1311 None => {
1312 let matcher = ContentModelMatcher::AllGroup(base_all_model);
1314 return Ok(attach_open_content(schema_set, matcher, open_content));
1315 }
1316 Some(particle) => {
1317 if let Some((ext_particles, ext_source)) = is_top_level_all_group(particle) {
1319 let base_outer_optional = base_all_model.outer_optional;
1322 let ext_outer_optional = particle.min_occurs == 0;
1323 if base_outer_optional != ext_outer_optional {
1324 return Err(NfaCompileError::InvalidAllGroupOccurs {
1325 reason: format!(
1326 "cos-ct-extends: when extending an xs:all base with an xs:all, \
1327 the outer minOccurs must match (base minOccurs={}, \
1328 extension minOccurs={})",
1329 if base_outer_optional { 0 } else { 1 },
1330 particle.min_occurs,
1331 ),
1332 location: particle.source.clone().or_else(|| ext_source.cloned()),
1333 });
1334 }
1335
1336 if base_all_model.particles.is_empty() {
1342 return Err(NfaCompileError::InvalidAllGroupContent {
1343 location: particle.source.clone().or_else(|| ext_source.cloned()),
1344 });
1345 }
1346
1347 let mut ctx = if upa_mode {
1348 CompileContext::new_for_upa(schema_set, type_def.target_namespace)
1349 } else {
1350 CompileContext::new(schema_set, type_def.target_namespace)
1351 };
1352 ctx.resolved_particle_types =
1353 type_def.resolved_content_particle_types.to_vec();
1354 ctx.resolved_particle_elements =
1355 type_def.resolved_content_particle_elements.to_vec();
1356 ctx.content_flat_idx = Some(0);
1357 let ext_model = ctx.compile_all_group_model(ext_particles, ext_source)?;
1358
1359 let merged_outer_optional = base_outer_optional && ext_outer_optional;
1360 let mut merged_particles = base_all_model.particles;
1361 merged_particles.extend(ext_model.particles);
1362 let mut merged = AllGroupModel::new(merged_particles);
1363 merged.outer_optional = merged_outer_optional;
1364 let matcher = ContentModelMatcher::AllGroup(merged);
1365 return Ok(attach_open_content(schema_set, matcher, open_content));
1366 }
1367
1368 return Err(NfaCompileError::InvalidAllGroupContent {
1373 location: particle.source.clone(),
1374 });
1375 }
1376 }
1377 }
1378 }
1379
1380 let own_nfa = match &type_def.content {
1382 ComplexContentResult::Complex(def) => match &def.particle {
1383 Some(particle) => {
1384 ctx.resolved_particle_types = type_def.resolved_content_particle_types.to_vec();
1385 ctx.resolved_particle_elements =
1386 type_def.resolved_content_particle_elements.to_vec();
1387 ctx.content_flat_idx = Some(0);
1388 Some(ctx.compile_particle(particle)?)
1389 }
1390 None => None,
1391 },
1392 ComplexContentResult::Empty | ComplexContentResult::Simple(_) => None,
1393 };
1394
1395 #[cfg(feature = "xsd11")]
1398 let mut inherited_oc: Option<OpenContent> = None;
1399 #[cfg(feature = "xsd11")]
1400 let mut base_target_ns: Option<NameId> = None;
1401
1402 let base_nfa = if is_extension {
1403 if let Some(TypeKey::Complex(base_ct_key)) = type_def.resolved_base_type {
1404 let base_type_def = &schema_set.arenas.complex_types[base_ct_key];
1405 #[cfg(feature = "xsd11")]
1406 {
1407 base_target_ns = base_type_def.target_namespace;
1408 }
1409 let base_matcher =
1410 compile_content_model_matcher_impl(schema_set, base_type_def, upa_mode)?;
1411 match base_matcher {
1412 ContentModelMatcher::Nfa(nfa) => Some(nfa),
1413 ContentModelMatcher::WithOpenContent {
1414 nfa,
1415 mode,
1416 wildcard,
1417 } => {
1418 #[cfg(feature = "xsd11")]
1419 {
1420 inherited_oc = Some(OpenContent {
1421 mode,
1422 wildcard,
1423 source: None,
1424 });
1425 }
1426 #[cfg(not(feature = "xsd11"))]
1427 let _ = (mode, wildcard);
1428 Some(nfa)
1429 }
1430 ContentModelMatcher::AllGroup(ref model) => {
1431 if own_nfa.is_none() {
1432 let open_content = resolve_open_content(
1435 schema_set,
1436 &type_def.content,
1437 type_def.open_content.as_ref(),
1438 type_def.source.as_ref(),
1439 );
1440 return Ok(attach_open_content(schema_set, base_matcher, open_content));
1441 }
1442 Some(all_group_to_nfa(model))
1445 }
1446 #[cfg(feature = "xsd11")]
1447 ContentModelMatcher::AllGroupExtension { .. } => {
1448 unreachable!("base type produced AllGroupExtension")
1450 }
1451 }
1452 } else {
1453 None
1454 }
1455 } else {
1456 None
1457 };
1458
1459 let effective_nfa = match (base_nfa, own_nfa) {
1460 (Some(base), Some(own)) => base.concat(own),
1461 (Some(base), None) => base,
1462 (None, Some(own)) => own,
1463 (None, None) => empty_nfa(),
1464 };
1465
1466 let base_matcher = ContentModelMatcher::Nfa(effective_nfa);
1467
1468 #[cfg(feature = "xsd11")]
1470 let open_content = if schema_set.is_xsd11() && is_extension {
1471 effective_open_content_for_extension(
1472 schema_set,
1473 type_def,
1474 base_target_ns,
1475 inherited_oc.as_ref(),
1476 )
1477 } else {
1478 resolve_open_content(
1479 schema_set,
1480 &type_def.content,
1481 type_def.open_content.as_ref(),
1482 type_def.source.as_ref(),
1483 )
1484 };
1485 #[cfg(not(feature = "xsd11"))]
1486 let open_content = resolve_open_content(
1487 schema_set,
1488 &type_def.content,
1489 type_def.open_content.as_ref(),
1490 type_def.source.as_ref(),
1491 );
1492
1493 Ok(attach_open_content(schema_set, base_matcher, open_content))
1494}
1495
1496#[cfg(feature = "xsd11")]
1498fn effective_open_content_for_extension(
1499 schema_set: &SchemaSet,
1500 type_def: &ComplexTypeDefData,
1501 base_target_ns: Option<NameId>,
1502 inherited: Option<&OpenContent>,
1503) -> Option<OpenContent> {
1504 let own_oc = resolve_open_content(
1505 schema_set,
1506 &type_def.content,
1507 type_def.open_content.as_ref(),
1508 type_def.source.as_ref(),
1509 );
1510 match own_oc {
1511 None => inherited.cloned(),
1513 Some(own) => {
1515 let Some(base_oc) = inherited else {
1516 return Some(own);
1517 };
1518 let derived_target_ns = type_def.target_namespace;
1519 let unioned_wildcard = match (own.wildcard.as_ref(), base_oc.wildcard.as_ref()) {
1520 (Some(own_wc), Some(base_wc)) => Some(wildcard_ref_union(
1521 base_wc,
1522 base_target_ns,
1523 own_wc,
1524 derived_target_ns,
1525 )),
1526 (Some(own_wc), None) => Some(own_wc.clone()),
1527 (None, Some(base_wc)) => Some(base_wc.clone()),
1528 (None, None) => None,
1529 };
1530 Some(OpenContent {
1531 mode: own.mode,
1532 wildcard: unioned_wildcard,
1533 source: own.source,
1534 })
1535 }
1536 }
1537}
1538
1539#[cfg(feature = "xsd11")]
1541fn wildcard_ref_union(
1542 base: &WildcardRef,
1543 base_target_ns: Option<NameId>,
1544 derived: &WildcardRef,
1545 derived_target_ns: Option<NameId>,
1546) -> WildcardRef {
1547 let c1 = expand_ns_constraint(&base.namespace_constraint, base_target_ns);
1548 let c2 = expand_ns_constraint(&derived.namespace_constraint, derived_target_ns);
1549 let union_ns = namespace_constraint_union(c1, c2);
1550
1551 let process_contents =
1552 less_restrictive_process_contents(base.process_contents, derived.process_contents);
1553
1554 let not_qnames: Vec<_> = base
1556 .not_qnames
1557 .iter()
1558 .filter(|q| derived.not_qnames.contains(q))
1559 .cloned()
1560 .collect();
1561
1562 WildcardRef {
1563 namespace_constraint: union_ns,
1564 process_contents,
1565 not_qnames,
1566 has_defined_sibling: false,
1567 source: derived.source.clone(),
1568 }
1569}
1570
1571#[cfg(feature = "xsd11")]
1573fn expand_ns_constraint(
1574 nc: &NamespaceConstraint,
1575 target_ns: Option<NameId>,
1576) -> NamespaceConstraint {
1577 match nc {
1578 NamespaceConstraint::Other => NamespaceConstraint::Not(vec![target_ns, None]),
1579 NamespaceConstraint::TargetNamespace => NamespaceConstraint::List(vec![target_ns]),
1580 NamespaceConstraint::Local => NamespaceConstraint::List(vec![None]),
1581 other => other.clone(),
1582 }
1583}
1584
1585#[cfg(feature = "xsd11")]
1587fn namespace_constraint_union(
1588 c1: NamespaceConstraint,
1589 c2: NamespaceConstraint,
1590) -> NamespaceConstraint {
1591 match (c1, c2) {
1592 (NamespaceConstraint::Any, _) | (_, NamespaceConstraint::Any) => NamespaceConstraint::Any,
1594 (NamespaceConstraint::Not(e1), NamespaceConstraint::Not(e2)) => {
1596 let intersection: Vec<_> = e1.iter().filter(|x| e2.contains(x)).cloned().collect();
1597 if intersection.is_empty() {
1598 NamespaceConstraint::Any
1599 } else {
1600 NamespaceConstraint::Not(intersection)
1601 }
1602 }
1603 (NamespaceConstraint::Not(e), NamespaceConstraint::List(s))
1605 | (NamespaceConstraint::List(s), NamespaceConstraint::Not(e)) => {
1606 let diff: Vec<_> = e.into_iter().filter(|x| !s.contains(x)).collect();
1607 if diff.is_empty() {
1608 NamespaceConstraint::Any
1609 } else {
1610 NamespaceConstraint::Not(diff)
1611 }
1612 }
1613 (NamespaceConstraint::List(mut a), NamespaceConstraint::List(b)) => {
1615 for x in b {
1616 if !a.contains(&x) {
1617 a.push(x);
1618 }
1619 }
1620 NamespaceConstraint::List(a)
1621 }
1622 (
1624 NamespaceConstraint::Other
1625 | NamespaceConstraint::TargetNamespace
1626 | NamespaceConstraint::Local,
1627 _,
1628 )
1629 | (
1630 _,
1631 NamespaceConstraint::Other
1632 | NamespaceConstraint::TargetNamespace
1633 | NamespaceConstraint::Local,
1634 ) => NamespaceConstraint::Any,
1635 }
1636}
1637
1638#[cfg(feature = "xsd11")]
1640fn less_restrictive_process_contents(
1641 a: TypesProcessContents,
1642 b: TypesProcessContents,
1643) -> TypesProcessContents {
1644 match (a, b) {
1645 (TypesProcessContents::Skip, _) | (_, TypesProcessContents::Skip) => {
1646 TypesProcessContents::Skip
1647 }
1648 (TypesProcessContents::Lax, _) | (_, TypesProcessContents::Lax) => {
1649 TypesProcessContents::Lax
1650 }
1651 _ => TypesProcessContents::Strict,
1652 }
1653}
1654
1655fn empty_nfa() -> NfaTable {
1656 let builder = FragmentBuilder::new();
1657 fragment_to_table(builder.epsilon_fragment())
1658}
1659
1660fn attach_open_content(
1661 schema_set: &SchemaSet,
1662 matcher: ContentModelMatcher,
1663 open_content: Option<OpenContent>,
1664) -> ContentModelMatcher {
1665 let open_content = match open_content {
1666 Some(open_content) => open_content,
1667 None => return matcher,
1668 };
1669
1670 match matcher {
1671 ContentModelMatcher::Nfa(nfa) => {
1672 let wildcard = open_content.wildcard.map(|mut w| {
1673 if w.has_defined_sibling {
1674 w.not_qnames
1675 .extend(collect_nfa_element_qnames(schema_set, &nfa));
1676 w.has_defined_sibling = false;
1677 }
1678 w
1679 });
1680 ContentModelMatcher::WithOpenContent {
1681 nfa,
1682 mode: open_content.mode,
1683 wildcard,
1684 }
1685 }
1686 ContentModelMatcher::AllGroup(mut model) => {
1687 if let Some(mut wildcard_ref) = open_content.wildcard {
1688 if wildcard_ref.has_defined_sibling {
1689 wildcard_ref
1690 .not_qnames
1691 .extend(collect_all_group_element_qnames(schema_set, &model));
1692 wildcard_ref.has_defined_sibling = false;
1693 }
1694 let mode = match open_content.mode {
1695 TypesOpenContentMode::Interleave => AllGroupOpenContentMode::Interleave,
1696 TypesOpenContentMode::Suffix => AllGroupOpenContentMode::Suffix,
1697 TypesOpenContentMode::None => AllGroupOpenContentMode::None,
1698 };
1699 model.open_content = Some(OpenContentWildcard {
1700 namespace_constraint: wildcard_ref.namespace_constraint,
1701 process_contents: wildcard_ref.process_contents,
1702 mode,
1703 not_qnames: wildcard_ref.not_qnames,
1704 });
1705 }
1706 ContentModelMatcher::AllGroup(model)
1707 }
1708 #[cfg(feature = "xsd11")]
1709 ContentModelMatcher::AllGroupExtension {
1710 mut base_model,
1711 extension_nfa,
1712 } => {
1713 if let Some(mut wildcard_ref) = open_content.wildcard {
1714 if wildcard_ref.has_defined_sibling {
1715 wildcard_ref
1717 .not_qnames
1718 .extend(collect_all_group_element_qnames(schema_set, &base_model));
1719 wildcard_ref
1720 .not_qnames
1721 .extend(collect_nfa_element_qnames(schema_set, &extension_nfa));
1722 wildcard_ref.has_defined_sibling = false;
1723 }
1724 let mode = match open_content.mode {
1725 TypesOpenContentMode::Interleave => AllGroupOpenContentMode::Interleave,
1726 TypesOpenContentMode::Suffix => AllGroupOpenContentMode::Suffix,
1727 TypesOpenContentMode::None => AllGroupOpenContentMode::None,
1728 };
1729 base_model.open_content = Some(OpenContentWildcard {
1730 namespace_constraint: wildcard_ref.namespace_constraint,
1731 process_contents: wildcard_ref.process_contents,
1732 mode,
1733 not_qnames: wildcard_ref.not_qnames,
1734 });
1735 }
1736 ContentModelMatcher::AllGroupExtension {
1737 base_model,
1738 extension_nfa,
1739 }
1740 }
1741 other => other,
1742 }
1743}
1744
1745fn resolve_open_content(
1746 schema_set: &SchemaSet,
1747 content: &ComplexContentResult,
1748 explicit: Option<&OpenContentResult>,
1749 source: Option<&SourceRef>,
1750) -> Option<OpenContent> {
1751 if !schema_set.is_xsd11() {
1752 return None;
1753 }
1754
1755 if let Some(explicit) = explicit {
1756 let target_namespace = source
1757 .and_then(|s| schema_set.documents.get(s.doc_id as usize))
1758 .and_then(|d| d.target_namespace);
1759 return open_content_from_result(explicit, schema_set, target_namespace);
1760 }
1761
1762 if !matches!(
1763 content,
1764 ComplexContentResult::Complex(_) | ComplexContentResult::Empty
1765 ) {
1766 return None;
1767 }
1768
1769 let doc = source.and_then(|s| schema_set.documents.get(s.defaults_doc() as usize));
1776 let default = doc.and_then(|d| d.default_open_content.as_ref())?;
1777
1778 if !default.applies_to_empty && content.explicit_content_type_is_empty() {
1785 return None;
1786 }
1787
1788 open_content_from_default(default, schema_set)
1789}
1790
1791fn open_content_from_result(
1792 result: &OpenContentResult,
1793 schema_set: &SchemaSet,
1794 target_namespace: Option<NameId>,
1795) -> Option<OpenContent> {
1796 let mode: TypesOpenContentMode = result.mode.into();
1797 if matches!(mode, TypesOpenContentMode::None) {
1798 return None;
1799 }
1800
1801 Some(OpenContent {
1802 mode,
1803 wildcard: result
1804 .wildcard
1805 .as_ref()
1806 .map(|w| wildcard_ref_from_result(w, schema_set, target_namespace)),
1807 source: result.source.clone(),
1808 })
1809}
1810
1811fn open_content_from_default(
1812 default: &DefaultOpenContent,
1813 schema_set: &SchemaSet,
1814) -> Option<OpenContent> {
1815 let mode: TypesOpenContentMode = default.mode.into();
1816 if matches!(mode, TypesOpenContentMode::None) {
1817 return None;
1818 }
1819
1820 Some(OpenContent {
1821 mode,
1822 wildcard: default
1823 .wildcard
1824 .as_ref()
1825 .map(|w| wildcard_ref_from_default(w, schema_set)),
1826 source: default.source.clone(),
1827 })
1828}
1829
1830fn expand_defined_element_qnames(schema_set: &SchemaSet) -> Vec<(Option<NameId>, NameId)> {
1832 schema_set
1833 .namespaces
1834 .iter()
1835 .flat_map(|(ns, table)| table.elements.keys().map(move |name| (*ns, *name)))
1836 .collect()
1837}
1838
1839fn collect_nfa_element_qnames(
1842 schema_set: &SchemaSet,
1843 nfa: &NfaTable,
1844) -> Vec<(Option<NameId>, NameId)> {
1845 let mut result = Vec::new();
1846 for state in &nfa.states {
1847 if let Some(NfaTerm::Element {
1848 namespace,
1849 name,
1850 element_key,
1851 ..
1852 }) = &state.term
1853 {
1854 let qname = (*namespace, *name);
1855 if !result.contains(&qname) {
1856 result.push(qname);
1857 }
1858 if let Some(head_key) = element_key {
1859 collect_substitution_members(schema_set, *head_key, &mut result);
1860 }
1861 }
1862 }
1863 result
1864}
1865
1866fn collect_all_group_element_qnames(
1869 schema_set: &SchemaSet,
1870 model: &AllGroupModel,
1871) -> Vec<(Option<NameId>, NameId)> {
1872 let mut result = Vec::new();
1873 for particle in &model.particles {
1874 if let NfaTerm::Element {
1875 namespace,
1876 name,
1877 element_key,
1878 ..
1879 } = &particle.term
1880 {
1881 let qname = (*namespace, *name);
1882 if !result.contains(&qname) {
1883 result.push(qname);
1884 }
1885 if let Some(head_key) = element_key {
1886 collect_substitution_members(schema_set, *head_key, &mut result);
1887 }
1888 }
1889 }
1890 result
1891}
1892
1893fn wildcard_ref_from_result(
1894 wildcard: &WildcardResult,
1895 schema_set: &SchemaSet,
1896 target_namespace: Option<NameId>,
1897) -> WildcardRef {
1898 let mut namespace_constraint = match &wildcard.namespace {
1899 WildcardNamespace::Any => NamespaceConstraint::Any,
1900 WildcardNamespace::Other => NamespaceConstraint::Other,
1901 WildcardNamespace::TargetNamespace => NamespaceConstraint::TargetNamespace,
1902 WildcardNamespace::Local => NamespaceConstraint::Local,
1903 WildcardNamespace::List(list) => {
1904 NamespaceConstraint::List(list.iter().map(|t| t.resolve(target_namespace)).collect())
1905 }
1906 };
1907
1908 if !wildcard.not_namespace.is_empty() {
1910 let excluded: Vec<Option<NameId>> = wildcard
1911 .not_namespace
1912 .iter()
1913 .map(|t| t.resolve(target_namespace))
1914 .collect();
1915 namespace_constraint = NamespaceConstraint::Not(excluded);
1916 }
1917
1918 let mut not_qnames: Vec<(Option<NameId>, NameId)> = Vec::new();
1920 let mut has_defined_sibling = false;
1921 for item in &wildcard.not_qname {
1922 match item {
1923 NotQNameItem::QName {
1924 namespace,
1925 local_name,
1926 } => {
1927 not_qnames.push((*namespace, *local_name));
1928 }
1929 NotQNameItem::Defined => {
1930 not_qnames.extend(expand_defined_element_qnames(schema_set));
1931 }
1932 NotQNameItem::DefinedSibling => {
1933 has_defined_sibling = true;
1936 }
1937 }
1938 }
1939
1940 let process_contents = match wildcard.process_contents {
1941 ProcessContents::Strict => TypesProcessContents::Strict,
1942 ProcessContents::Lax => TypesProcessContents::Lax,
1943 ProcessContents::Skip => TypesProcessContents::Skip,
1944 };
1945
1946 WildcardRef {
1947 namespace_constraint,
1948 process_contents,
1949 not_qnames,
1950 has_defined_sibling,
1951 source: wildcard.source.clone(),
1952 }
1953}
1954
1955fn wildcard_ref_from_default(wildcard: &ElementWildcard, schema_set: &SchemaSet) -> WildcardRef {
1956 let namespace_constraint = match &wildcard.namespace_constraint {
1957 SchemaNamespaceConstraint::Any => NamespaceConstraint::Any,
1958 SchemaNamespaceConstraint::Other => NamespaceConstraint::Other,
1959 SchemaNamespaceConstraint::Enumeration(list) => NamespaceConstraint::List(list.clone()),
1960 SchemaNamespaceConstraint::Not(excluded) => NamespaceConstraint::Not(excluded.clone()),
1961 };
1962
1963 let mut not_qnames: Vec<(Option<NameId>, NameId)> = Vec::new();
1965 let mut has_defined_sibling = false;
1966 for item in &wildcard.not_qnames {
1967 match item {
1968 crate::schema::wildcard::QNameDisallowed::QName {
1969 namespace,
1970 local_name,
1971 } => {
1972 not_qnames.push((*namespace, *local_name));
1973 }
1974 crate::schema::wildcard::QNameDisallowed::Defined => {
1975 not_qnames.extend(expand_defined_element_qnames(schema_set));
1976 }
1977 crate::schema::wildcard::QNameDisallowed::DefinedSibling => {
1978 has_defined_sibling = true;
1981 }
1982 }
1983 }
1984
1985 WildcardRef {
1986 namespace_constraint,
1987 process_contents: wildcard.process_contents,
1988 not_qnames,
1989 has_defined_sibling,
1990 source: wildcard.source.clone(),
1991 }
1992}
1993
1994#[cfg(test)]
1995#[path = "compile_tests.rs"]
1996mod tests;