use crate::arenas::{
ComplexTypeDefData, ElementDeclData, IdentityConstraintData, ResolvedAttributeUse,
SimpleTypeDefData,
};
use std::collections::{HashMap, HashSet};
use crate::error::{SchemaError, SchemaResult};
use crate::ids::*;
use crate::namespace::NameTable;
use crate::parser::frames::{
ComplexContentResult, ElementFrameResult, IdentityKind, IdentityResult, ParticleResult,
ParticleTerm, QNameRef, TypeFrameResult, TypeRefResult,
};
use crate::parser::location::{SourceMapStorage, SourceRef};
use crate::schema::model::DerivationSet;
use crate::schema::SchemaSet;
fn resolve_derivation_default(
parsed: Option<DerivationSet>,
source: Option<&SourceRef>,
schema_set: &SchemaSet,
pick_default: impl Fn(&crate::schema::model::SchemaDocument) -> DerivationSet,
) -> DerivationSet {
parsed.unwrap_or_else(|| {
source
.and_then(|s| {
let doc_id = s.schema_defaults_doc.unwrap_or(s.doc_id);
schema_set.documents.get(doc_id as usize)
})
.map(pick_default)
.unwrap_or_default()
})
}
fn resolve_block(
block: Option<DerivationSet>,
source: Option<&SourceRef>,
schema_set: &SchemaSet,
) -> DerivationSet {
resolve_derivation_default(block, source, schema_set, |d| d.block_default)
}
fn resolve_final(
final_derivation: Option<DerivationSet>,
source: Option<&SourceRef>,
schema_set: &SchemaSet,
) -> DerivationSet {
resolve_derivation_default(final_derivation, source, schema_set, |d| d.final_default)
}
#[derive(Debug, Default)]
pub struct InlineAssemblyStats {
pub element_inline_types: usize,
pub attribute_inline_types: usize,
pub simple_type_inline_derivations: usize,
pub complex_type_inline_derivations: usize,
pub model_group_inline_types: usize,
pub attribute_group_inline_types: usize,
pub complex_type_attribute_inline_types: usize,
pub total_inline_types: usize,
}
#[derive(Debug, Clone, Copy)]
enum InlineRole {
ElementType,
AttributeType,
SimpleTypeBase,
ListItemType,
UnionMemberType(usize),
ComplexTypeBase,
ComplexTypeAttribute(usize),
ModelGroupParticle(usize),
AttributeGroupAttribute(usize),
ContentParticleType(usize),
#[cfg(feature = "xsd11")]
AlternativeType(usize),
SimpleContentInlineType,
}
#[derive(Debug, Clone, Copy)]
enum InlineOwner {
Element(ElementKey),
Attribute(AttributeKey),
SimpleType(SimpleTypeKey),
ComplexType(ComplexTypeKey),
ModelGroup(ModelGroupKey),
AttributeGroup(AttributeGroupKey),
}
struct InlineTypeJob {
owner: InlineOwner,
role: InlineRole,
type_frame: TypeFrameResult,
target_namespace: Option<NameId>,
}
pub fn assemble_inline_types(schema_set: &mut SchemaSet) -> SchemaResult<InlineAssemblyStats> {
let mut stats = InlineAssemblyStats::default();
let mut jobs = collect_inline_type_jobs(schema_set);
while !jobs.is_empty() {
let current_jobs: Vec<_> = std::mem::take(&mut jobs);
for job in current_jobs {
let type_key = assemble_inline_type(schema_set, &job.type_frame, job.target_namespace)?;
update_owner(schema_set, &job, type_key, &mut stats)?;
collect_nested_inline_types(schema_set, type_key, job.target_namespace, &mut jobs);
}
}
Ok(stats)
}
fn collect_inline_type_jobs(schema_set: &SchemaSet) -> Vec<InlineTypeJob> {
let mut jobs = Vec::new();
for (key, elem) in schema_set.arenas.elements.iter() {
let target_ns = elem.target_namespace;
if let Some(inline_type) = &elem.inline_type {
jobs.push(InlineTypeJob {
owner: InlineOwner::Element(key),
role: InlineRole::ElementType,
type_frame: (**inline_type).clone(),
target_namespace: target_ns,
});
}
}
#[cfg(feature = "xsd11")]
for (key, elem) in schema_set.arenas.elements.iter() {
let target_ns = elem.target_namespace;
for (idx, alt) in elem.alternatives.iter().enumerate() {
if let Some(inline_type) = &alt.inline_type {
jobs.push(InlineTypeJob {
owner: InlineOwner::Element(key),
role: InlineRole::AlternativeType(idx),
type_frame: (**inline_type).clone(),
target_namespace: target_ns,
});
}
}
}
for (key, attr) in schema_set.arenas.attributes.iter() {
let target_ns = attr.target_namespace;
if let Some(inline_type) = &attr.inline_type {
jobs.push(InlineTypeJob {
owner: InlineOwner::Attribute(key),
role: InlineRole::AttributeType,
type_frame: TypeFrameResult::Simple(Box::new((**inline_type).clone())),
target_namespace: target_ns,
});
}
}
for (key, simple) in schema_set.arenas.simple_types.iter() {
let target_ns = simple.target_namespace;
if let Some(TypeRefResult::Inline(inline_type)) = &simple.base_type {
jobs.push(InlineTypeJob {
owner: InlineOwner::SimpleType(key),
role: InlineRole::SimpleTypeBase,
type_frame: (**inline_type).clone(),
target_namespace: target_ns,
});
}
if let Some(TypeRefResult::Inline(inline_type)) = &simple.item_type {
jobs.push(InlineTypeJob {
owner: InlineOwner::SimpleType(key),
role: InlineRole::ListItemType,
type_frame: (**inline_type).clone(),
target_namespace: target_ns,
});
}
for (idx, member) in simple.member_types.iter().enumerate() {
if let TypeRefResult::Inline(inline_type) = member {
jobs.push(InlineTypeJob {
owner: InlineOwner::SimpleType(key),
role: InlineRole::UnionMemberType(idx),
type_frame: (**inline_type).clone(),
target_namespace: target_ns,
});
}
}
}
for (key, complex) in schema_set.arenas.complex_types.iter() {
let target_ns = complex.target_namespace;
if let Some(TypeRefResult::Inline(inline_type)) = &complex.base_type {
jobs.push(InlineTypeJob {
owner: InlineOwner::ComplexType(key),
role: InlineRole::ComplexTypeBase,
type_frame: (**inline_type).clone(),
target_namespace: target_ns,
});
}
for (idx, attr_use) in complex.attributes.iter().enumerate() {
if let Some(inline_type) = &attr_use.attribute.inline_type {
jobs.push(InlineTypeJob {
owner: InlineOwner::ComplexType(key),
role: InlineRole::ComplexTypeAttribute(idx),
type_frame: TypeFrameResult::Simple(Box::new((**inline_type).clone())),
target_namespace: target_ns,
});
}
}
collect_content_inline_types(&complex.content, key, target_ns, &mut jobs);
}
for (key, group) in schema_set.arenas.model_groups.iter() {
let target_ns = group.target_namespace;
let mut flat_idx = 0;
collect_model_group_inline_types_recursive(
&group.particles,
key,
target_ns,
&mut flat_idx,
&mut jobs,
);
}
for (key, group) in schema_set.arenas.attribute_groups.iter() {
let target_ns = group.target_namespace;
for (idx, attr_use) in group.attributes.iter().enumerate() {
if let Some(inline_type) = &attr_use.attribute.inline_type {
jobs.push(InlineTypeJob {
owner: InlineOwner::AttributeGroup(key),
role: InlineRole::AttributeGroupAttribute(idx),
type_frame: TypeFrameResult::Simple(Box::new((**inline_type).clone())),
target_namespace: target_ns,
});
}
}
}
jobs
}
fn collect_content_inline_types(
content: &ComplexContentResult,
owner_key: ComplexTypeKey,
target_ns: Option<NameId>,
jobs: &mut Vec<InlineTypeJob>,
) {
match content {
ComplexContentResult::Complex(complex_content) => {
if let Some(TypeRefResult::Inline(inline_type)) = &complex_content.base_type {
jobs.push(InlineTypeJob {
owner: InlineOwner::ComplexType(owner_key),
role: InlineRole::ComplexTypeBase,
type_frame: (**inline_type).clone(),
target_namespace: target_ns,
});
}
for (idx, attr_use) in complex_content.attributes.iter().enumerate() {
if let Some(inline_type) = &attr_use.attribute.inline_type {
jobs.push(InlineTypeJob {
owner: InlineOwner::ComplexType(owner_key),
role: InlineRole::ComplexTypeAttribute(idx),
type_frame: TypeFrameResult::Simple(Box::new((**inline_type).clone())),
target_namespace: target_ns,
});
}
}
if let Some(particle) = &complex_content.particle {
collect_particle_inline_types(particle, owner_key, target_ns, jobs);
}
}
ComplexContentResult::Simple(simple_content) => {
if let Some(TypeRefResult::Inline(inline_type)) = &simple_content.base_type {
jobs.push(InlineTypeJob {
owner: InlineOwner::ComplexType(owner_key),
role: InlineRole::ComplexTypeBase,
type_frame: (**inline_type).clone(),
target_namespace: target_ns,
});
}
for (idx, attr_use) in simple_content.attributes.iter().enumerate() {
if let Some(inline_type) = &attr_use.attribute.inline_type {
jobs.push(InlineTypeJob {
owner: InlineOwner::ComplexType(owner_key),
role: InlineRole::ComplexTypeAttribute(idx),
type_frame: TypeFrameResult::Simple(Box::new((**inline_type).clone())),
target_namespace: target_ns,
});
}
}
if let Some(inline_st) = &simple_content.content_type {
jobs.push(InlineTypeJob {
owner: InlineOwner::ComplexType(owner_key),
role: InlineRole::SimpleContentInlineType,
type_frame: TypeFrameResult::Simple(Box::new((**inline_st).clone())),
target_namespace: target_ns,
});
}
}
ComplexContentResult::Empty => {}
}
}
fn collect_particle_inline_types(
particle: &ParticleResult,
owner_key: ComplexTypeKey,
target_ns: Option<NameId>,
jobs: &mut Vec<InlineTypeJob>,
) {
if let ParticleTerm::Group(group_def) = &particle.term {
let mut flat_idx = 0;
collect_group_elements_recursive(
&group_def.particles,
owner_key,
target_ns,
&mut flat_idx,
jobs,
);
}
}
fn collect_group_elements_recursive(
particles: &[ParticleResult],
owner_key: ComplexTypeKey,
target_ns: Option<NameId>,
flat_idx: &mut usize,
jobs: &mut Vec<InlineTypeJob>,
) {
for particle in particles {
match &particle.term {
ParticleTerm::Element(elem) => {
if let Some(inline_type) = &elem.inline_type {
jobs.push(InlineTypeJob {
owner: InlineOwner::ComplexType(owner_key),
role: InlineRole::ContentParticleType(*flat_idx),
type_frame: (**inline_type).clone(),
target_namespace: target_ns,
});
}
*flat_idx += 1;
}
ParticleTerm::Group(group_def) if group_def.ref_name.is_none() => {
collect_group_elements_recursive(
&group_def.particles,
owner_key,
target_ns,
flat_idx,
jobs,
);
}
_ => {} }
}
}
fn collect_model_group_inline_types_recursive(
particles: &[ParticleResult],
owner_key: ModelGroupKey,
target_ns: Option<NameId>,
flat_idx: &mut usize,
jobs: &mut Vec<InlineTypeJob>,
) {
for particle in particles {
match &particle.term {
ParticleTerm::Element(elem) => {
if let Some(inline_type) = &elem.inline_type {
jobs.push(InlineTypeJob {
owner: InlineOwner::ModelGroup(owner_key),
role: InlineRole::ModelGroupParticle(*flat_idx),
type_frame: (**inline_type).clone(),
target_namespace: target_ns,
});
}
*flat_idx += 1;
}
ParticleTerm::Group(group_def) if group_def.ref_name.is_none() => {
collect_model_group_inline_types_recursive(
&group_def.particles,
owner_key,
target_ns,
flat_idx,
jobs,
);
}
_ => {} }
}
}
struct ContentParticleElementJob {
complex_type_key: ComplexTypeKey,
flat_idx: usize,
elem: ElementFrameResult,
target_namespace: Option<NameId>,
}
fn collect_content_particle_elements_recursive(
particles: &[ParticleResult],
complex_type_key: ComplexTypeKey,
target_ns: Option<NameId>,
flat_idx: &mut usize,
jobs: &mut Vec<ContentParticleElementJob>,
) {
for particle in particles {
match &particle.term {
ParticleTerm::Element(elem) if elem.ref_name.is_none() => {
jobs.push(ContentParticleElementJob {
complex_type_key,
flat_idx: *flat_idx,
elem: elem.clone(),
target_namespace: target_ns,
});
*flat_idx += 1;
}
ParticleTerm::Element(_) => {
*flat_idx += 1;
}
ParticleTerm::Group(group_def) if group_def.ref_name.is_none() => {
collect_content_particle_elements_recursive(
&group_def.particles,
complex_type_key,
target_ns,
flat_idx,
jobs,
);
}
_ => {} }
}
}
fn check_keyref_target(
ic: &IdentityResult,
target_kind: IdentityKind,
target_field_count: usize,
refer_name: NameId,
name_table: &NameTable,
source_maps: &SourceMapStorage,
) -> SchemaResult<()> {
if target_kind == IdentityKind::Keyref {
let ic_name = name_table.resolve_ref(ic.name);
let refer_name_str = name_table.resolve_ref(refer_name);
let location = ic.source.as_ref().and_then(|s| source_maps.locate(s));
return Err(SchemaError::structural(
"src-identity-constraint",
format!(
"Keyref '{}': refer target '{}' is a keyref, not a key or unique",
ic_name, refer_name_str
),
location,
));
}
if ic.fields.len() != target_field_count {
let ic_name = name_table.resolve_ref(ic.name);
let refer_name_str = name_table.resolve_ref(refer_name);
let location = ic.source.as_ref().and_then(|s| source_maps.locate(s));
return Err(SchemaError::structural(
"src-identity-constraint",
format!(
"Keyref '{}': has {} field(s) but refer target '{}' has {} field(s)",
ic_name,
ic.fields.len(),
refer_name_str,
target_field_count
),
location,
));
}
Ok(())
}
pub(crate) fn resolve_ic_ref(
kind: IdentityKind,
ref_name: &QNameRef,
source: Option<&SourceRef>,
target_namespace: Option<NameId>,
schema_set: &crate::schema::SchemaSet,
) -> crate::error::SchemaResult<IdentityConstraintKey> {
let ref_ns = ref_name.namespace.or(target_namespace);
let ref_local = ref_name.local_name;
crate::schema::resolver::check_namespace_visible_ns(
schema_set,
ref_ns,
ref_local,
source,
"Identity constraint",
)?;
let target_key = schema_set
.namespaces
.get(&ref_ns)
.and_then(|nt| nt.identity_constraints.get(&ref_local))
.copied();
let target_key = match target_key {
Some(k) => k,
None => {
let mut found = None;
for (key, ic_data) in &schema_set.arenas.identity_constraints {
if ic_data.name != ref_local {
continue;
}
let ic_ns = ic_data
.source
.as_ref()
.and_then(|s| schema_set.documents.get(s.doc_id as usize))
.and_then(|d| d.target_namespace);
if ic_ns == ref_ns {
found = Some(key);
break;
}
}
found.ok_or_else(|| {
let ref_display = crate::schema::resolver::format_resolved_qname(
&schema_set.name_table,
ref_ns,
ref_local,
);
let location = source.and_then(|s| schema_set.source_maps.locate(s));
crate::error::SchemaError::structural(
"src-resolve",
format!("Identity constraint ref target '{}' not found", ref_display),
location,
)
})?
}
};
let target = &schema_set.arenas.identity_constraints[target_key];
if target.kind != kind {
let ref_display = crate::schema::resolver::format_resolved_qname(
&schema_set.name_table,
ref_ns,
ref_local,
);
let location = source.and_then(|s| schema_set.source_maps.locate(s));
return Err(crate::error::SchemaError::structural(
"src-identity-constraint.5",
format!(
"Identity constraint ref '{}': referenced constraint is {:?} but expected {:?}",
ref_display, target.kind, kind
),
location,
));
}
Ok(target_key)
}
pub(crate) fn validate_keyref_refers(
identity_constraints: &[IdentityResult],
target_namespace: Option<NameId>,
name_table: &NameTable,
source_maps: &SourceMapStorage,
ic_arena: &slotmap::SlotMap<IdentityConstraintKey, IdentityConstraintData>,
documents: &[crate::schema::model::SchemaDocument],
) -> SchemaResult<()> {
for ic in identity_constraints {
if ic.kind != IdentityKind::Keyref {
continue;
}
if let Some(refer) = &ic.refer {
let refer_name = refer.local_name;
let refer_ns = refer.namespace;
let target = identity_constraints.iter().find(|other| {
other.name == refer_name && (refer_ns.is_none() || refer_ns == target_namespace)
});
match target {
None => {
let effective_refer_ns = refer_ns.or(target_namespace);
let global_target = ic_arena.values().find(|ic_data| {
if ic_data.name != refer_name {
return false;
}
let ic_ns = match ic_data
.source
.as_ref()
.map(|s| documents.get(s.doc_id as usize))
{
Some(Some(d)) => d.target_namespace,
Some(None) => target_namespace,
None => None,
};
effective_refer_ns == ic_ns
});
match global_target {
Some(target_data) => {
check_keyref_target(
ic,
target_data.kind,
target_data.fields.len(),
refer_name,
name_table,
source_maps,
)?;
}
None => {
let ic_name = name_table.resolve_ref(ic.name);
let refer_name_str = name_table.resolve_ref(refer_name);
let location = ic.source.as_ref().and_then(|s| source_maps.locate(s));
return Err(SchemaError::structural(
"src-identity-constraint",
format!(
"Keyref '{}': refer target '{}' not found among identity constraints",
ic_name, refer_name_str
),
location,
));
}
}
}
Some(target_ic) => {
check_keyref_target(
ic,
target_ic.kind,
target_ic.fields.len(),
refer_name,
name_table,
source_maps,
)?;
}
}
}
}
Ok(())
}
pub fn allocate_content_particle_elements(schema_set: &mut SchemaSet) -> SchemaResult<()> {
let mut jobs = Vec::new();
for (key, complex) in schema_set.arenas.complex_types.iter() {
let target_ns = complex.target_namespace;
if let ComplexContentResult::Complex(def) = &complex.content {
if let Some(particle) = &def.particle {
if let ParticleTerm::Group(group_def) = &particle.term {
let mut flat_idx = 0;
collect_content_particle_elements_recursive(
&group_def.particles,
key,
target_ns,
&mut flat_idx,
&mut jobs,
);
}
}
}
}
jobs.retain(|job| {
match schema_set
.arenas
.complex_types
.get(job.complex_type_key)
.and_then(|ct| ct.resolved_content_particle_elements.get(job.flat_idx))
{
Some(Some(_)) => false, _ => true,
}
});
let mut ic_names_by_doc: HashMap<DocumentId, HashSet<NameId>> = HashMap::new();
for ic in schema_set.arenas.identity_constraints.values() {
if let Some(source) = &ic.source {
ic_names_by_doc
.entry(source.doc_id)
.or_default()
.insert(ic.name);
}
}
for job in jobs {
if let Some(TypeRefResult::QName(qname)) = &job.elem.type_ref {
crate::schema::resolver::check_namespace_visible(
schema_set,
qname,
job.elem.source.as_ref(),
"Type",
)?;
}
let resolved_type = schema_set
.arenas
.complex_types
.get(job.complex_type_key)
.and_then(|ct| {
ct.resolved_content_particle_types
.get(job.flat_idx)
.copied()
.flatten()
})
.or_else(|| {
match &job.elem.type_ref {
Some(TypeRefResult::QName(qname)) => schema_set
.lookup_type(qname.namespace, qname.local_name)
.or_else(|| {
schema_set.get_built_in_type_by_qname(qname.namespace, qname.local_name)
}),
_ => None,
}
});
let resolved_type = resolved_type.or_else(|| match &job.elem.type_ref {
None => Some(TypeKey::Complex(schema_set.any_type_key())),
_ => None,
});
if resolved_type.is_none() {
if let Some(TypeRefResult::QName(qname)) = &job.elem.type_ref {
let display = crate::schema::resolver::format_resolved_qname(
&schema_set.name_table,
qname.namespace,
qname.local_name,
);
let location = schema_set.locate(job.elem.source.as_ref());
return Err(SchemaError::structural(
"src-resolve",
format!("Type reference '{}' on local element not found", display),
location,
));
}
}
let effective_ns = schema_set.effective_local_element_namespace(
job.elem.target_namespace,
job.elem.form.as_deref(),
job.elem.source.as_ref(),
job.target_namespace,
);
validate_keyref_refers(
&job.elem.identity_constraints,
job.target_namespace,
&schema_set.name_table,
&schema_set.source_maps,
&schema_set.arenas.identity_constraints,
&schema_set.documents,
)?;
let mut identity_constraint_keys = Vec::with_capacity(job.elem.identity_constraints.len());
for ic in job.elem.identity_constraints {
if let Some(source) = &ic.source {
let doc_names = ic_names_by_doc.entry(source.doc_id).or_default();
if !doc_names.insert(ic.name) {
let location = schema_set.source_maps.locate(source);
let name_str = schema_set.name_table.resolve(ic.name);
return Err(SchemaError::structural(
"ic-unique",
format!(
"Duplicate identity constraint name '{}' in schema document",
name_str
),
location,
));
}
}
let ic_name = ic.name;
let ic_key = schema_set
.arenas
.alloc_identity_constraint(IdentityConstraintData {
kind: ic.kind,
name: ic.name,
ref_name: ic.ref_name,
refer: ic.refer,
selector: ic.selector,
fields: ic.fields,
id: ic.id,
annotation: ic.annotation,
source: ic.source,
});
let ns_table = schema_set.get_or_create_namespace(job.target_namespace);
ns_table.identity_constraints.insert(ic_name, ic_key);
identity_constraint_keys.push(ic_key);
}
for ic_ref in &job.elem.identity_constraint_refs {
let target_key = resolve_ic_ref(
ic_ref.kind,
&ic_ref.ref_name,
ic_ref.source.as_ref(),
job.target_namespace,
schema_set,
)?;
identity_constraint_keys.push(target_key);
}
let block = resolve_block(job.elem.block, job.elem.source.as_ref(), schema_set);
let final_derivation = resolve_final(
job.elem.final_derivation,
job.elem.source.as_ref(),
schema_set,
);
let elem_data = ElementDeclData {
name: job.elem.name,
target_namespace: effective_ns,
ref_name: None,
type_ref: job.elem.type_ref.clone(),
inline_type: job.elem.inline_type.clone(),
substitution_group: Vec::new(),
default_value: job.elem.default_value.clone(),
fixed_value: job.elem.fixed_value.clone(),
nillable: job.elem.nillable,
is_abstract: job.elem.is_abstract,
min_occurs: job.elem.min_occurs,
max_occurs: job.elem.max_occurs,
block,
final_derivation,
form: job.elem.form.clone(),
id: job.elem.id.clone(),
alternatives: job.elem.alternatives.clone(),
identity_constraints: identity_constraint_keys,
pending_ic_refs: vec![],
annotation: job.elem.annotation.clone(),
source: job.elem.source.clone(),
resolved_type,
resolved_ref: None,
resolved_substitution_groups: Vec::new(),
deferred_type_error: None,
};
let elem_key = schema_set.arenas.alloc_element(elem_data);
if let Some(ct) = schema_set
.arenas
.complex_types
.get_mut(job.complex_type_key)
{
while ct.resolved_content_particle_elements.len() <= job.flat_idx {
ct.resolved_content_particle_elements.push(None);
}
ct.resolved_content_particle_elements[job.flat_idx] = Some(elem_key);
}
}
Ok(())
}
#[cfg(feature = "xsd11")]
pub fn resolve_local_element_alternatives(
schema_set: &mut SchemaSet,
) -> SchemaResult<Vec<ComplexTypeKey>> {
use crate::ids::ComplexTypeKey;
let mut qname_pending: Vec<(ElementKey, usize, QNameRef, Option<SourceRef>)> = Vec::new();
for (key, elem) in schema_set.arenas.elements.iter() {
for (idx, alt) in elem.alternatives.iter().enumerate() {
if alt.resolved_type.is_some() {
continue;
}
if let Some(TypeRefResult::QName(qname)) = &alt.type_ref {
qname_pending.push((key, idx, qname.clone(), alt.source.clone()));
}
}
}
for (elem_key, alt_idx, qname, src) in qname_pending {
let resolver = crate::schema::resolver::ReferenceResolver::new(schema_set);
let type_key = resolver.resolve_type_ref(&qname, src.as_ref())?;
if let Some(elem) = schema_set.arenas.elements.get_mut(elem_key) {
if let Some(alt) = elem.alternatives.get_mut(alt_idx) {
alt.resolved_type = Some(type_key);
}
}
}
let mut pending: Vec<(ElementKey, usize, TypeFrameResult, Option<NameId>)> = Vec::new();
for (key, elem) in schema_set.arenas.elements.iter() {
for (idx, alt) in elem.alternatives.iter().enumerate() {
if alt.resolved_type.is_some() {
continue;
}
let Some(inline_type) = &alt.inline_type else {
continue;
};
pending.push((key, idx, (**inline_type).clone(), elem.target_namespace));
}
}
let mut new_complex_keys: Vec<ComplexTypeKey> = Vec::new();
for (elem_key, alt_idx, type_frame, target_ns) in pending {
let type_key = assemble_inline_type(schema_set, &type_frame, target_ns)?;
if let TypeKey::Complex(ct_key) = type_key {
new_complex_keys.push(ct_key);
}
if let Some(elem) = schema_set.arenas.elements.get_mut(elem_key) {
if let Some(alt) = elem.alternatives.get_mut(alt_idx) {
alt.resolved_type = Some(type_key);
}
}
}
Ok(new_complex_keys)
}
struct ModelGroupElementJob {
group_key: ModelGroupKey,
particle_idx: usize,
elem: ElementFrameResult,
target_namespace: Option<NameId>,
}
pub fn allocate_model_group_particle_elements(schema_set: &mut SchemaSet) -> SchemaResult<()> {
let mut jobs = Vec::new();
for (key, group) in schema_set.arenas.model_groups.iter() {
let target_ns = group.target_namespace;
let mut flat_idx = 0;
collect_model_group_elements_recursive(
&group.particles,
key,
target_ns,
&mut flat_idx,
&mut jobs,
);
}
let mut ic_names_by_doc: HashMap<DocumentId, HashSet<NameId>> = HashMap::new();
for ic in schema_set.arenas.identity_constraints.values() {
if let Some(source) = &ic.source {
ic_names_by_doc
.entry(source.doc_id)
.or_default()
.insert(ic.name);
}
}
for job in jobs {
if let Some(TypeRefResult::QName(qname)) = &job.elem.type_ref {
crate::schema::resolver::check_namespace_visible(
schema_set,
qname,
job.elem.source.as_ref(),
"Type",
)?;
}
let resolved_type = schema_set
.arenas
.model_groups
.get(job.group_key)
.and_then(|g| {
g.resolved_particle_types
.get(job.particle_idx)
.copied()
.flatten()
})
.or_else(|| {
match &job.elem.type_ref {
Some(TypeRefResult::QName(qname)) => schema_set
.lookup_type(qname.namespace, qname.local_name)
.or_else(|| {
schema_set.get_built_in_type_by_qname(qname.namespace, qname.local_name)
}),
_ => None,
}
});
let effective_ns = schema_set.effective_local_element_namespace(
job.elem.target_namespace,
job.elem.form.as_deref(),
job.elem.source.as_ref(),
job.target_namespace,
);
validate_keyref_refers(
&job.elem.identity_constraints,
job.target_namespace,
&schema_set.name_table,
&schema_set.source_maps,
&schema_set.arenas.identity_constraints,
&schema_set.documents,
)?;
let mut identity_constraint_keys = Vec::with_capacity(job.elem.identity_constraints.len());
for ic in job.elem.identity_constraints {
if let Some(source) = &ic.source {
let doc_names = ic_names_by_doc.entry(source.doc_id).or_default();
if !doc_names.insert(ic.name) {
let location = schema_set.source_maps.locate(source);
let name_str = schema_set.name_table.resolve(ic.name);
return Err(SchemaError::structural(
"ic-unique",
format!(
"Duplicate identity constraint name '{}' in schema document",
name_str
),
location,
));
}
}
let ic_name = ic.name;
let ic_key = schema_set
.arenas
.alloc_identity_constraint(IdentityConstraintData {
kind: ic.kind,
name: ic.name,
ref_name: ic.ref_name,
refer: ic.refer,
selector: ic.selector,
fields: ic.fields,
id: ic.id,
annotation: ic.annotation,
source: ic.source,
});
let ns_table = schema_set.get_or_create_namespace(job.target_namespace);
ns_table.identity_constraints.insert(ic_name, ic_key);
identity_constraint_keys.push(ic_key);
}
for ic_ref in &job.elem.identity_constraint_refs {
let target_key = resolve_ic_ref(
ic_ref.kind,
&ic_ref.ref_name,
ic_ref.source.as_ref(),
job.target_namespace,
schema_set,
)?;
identity_constraint_keys.push(target_key);
}
let block = resolve_block(job.elem.block, job.elem.source.as_ref(), schema_set);
let final_derivation = resolve_final(
job.elem.final_derivation,
job.elem.source.as_ref(),
schema_set,
);
let elem_data = ElementDeclData {
name: job.elem.name,
target_namespace: effective_ns,
ref_name: None,
type_ref: job.elem.type_ref.clone(),
inline_type: job.elem.inline_type.clone(),
substitution_group: Vec::new(),
default_value: job.elem.default_value.clone(),
fixed_value: job.elem.fixed_value.clone(),
nillable: job.elem.nillable,
is_abstract: job.elem.is_abstract,
min_occurs: job.elem.min_occurs,
max_occurs: job.elem.max_occurs,
block,
final_derivation,
form: job.elem.form.clone(),
id: job.elem.id.clone(),
alternatives: job.elem.alternatives.clone(),
identity_constraints: identity_constraint_keys,
pending_ic_refs: vec![],
annotation: job.elem.annotation.clone(),
source: job.elem.source.clone(),
resolved_type,
resolved_ref: None,
resolved_substitution_groups: Vec::new(),
deferred_type_error: None,
};
let elem_key = schema_set.arenas.alloc_element(elem_data);
if let Some(group) = schema_set.arenas.model_groups.get_mut(job.group_key) {
while group.resolved_particle_elements.len() <= job.particle_idx {
group.resolved_particle_elements.push(None);
}
group.resolved_particle_elements[job.particle_idx] = Some(elem_key);
}
}
Ok(())
}
fn collect_model_group_elements_recursive(
particles: &[ParticleResult],
group_key: ModelGroupKey,
target_ns: Option<NameId>,
flat_idx: &mut usize,
jobs: &mut Vec<ModelGroupElementJob>,
) {
for particle in particles {
match &particle.term {
ParticleTerm::Element(elem) if elem.ref_name.is_none() => {
jobs.push(ModelGroupElementJob {
group_key,
particle_idx: *flat_idx,
elem: elem.clone(),
target_namespace: target_ns,
});
*flat_idx += 1;
}
ParticleTerm::Element(_) => {
*flat_idx += 1;
}
ParticleTerm::Group(group_def) if group_def.ref_name.is_none() => {
collect_model_group_elements_recursive(
&group_def.particles,
group_key,
target_ns,
flat_idx,
jobs,
);
}
_ => {} }
}
}
fn assemble_inline_type(
schema_set: &mut SchemaSet,
type_frame: &TypeFrameResult,
target_namespace: Option<NameId>,
) -> SchemaResult<TypeKey> {
match type_frame {
TypeFrameResult::Simple(simple) => {
let final_derivation =
resolve_final(simple.final_derivation, simple.source.as_ref(), schema_set);
let data = SimpleTypeDefData {
name: simple.name, target_namespace,
variety: simple.variety,
base_type: simple.base_type.clone(),
item_type: simple.item_type.clone(),
member_types: simple.member_types.clone(),
facets: simple.facets.clone(),
final_derivation,
id: simple.id.clone(),
derivation_id: simple.derivation_id.clone(),
annotation: simple.annotation.clone(),
source: simple.source.clone(),
resolved_base_type: None,
resolved_item_type: None,
resolved_member_types: Vec::new(),
redefine_original: None,
deferred_item_type_error: None,
};
let key = schema_set.arenas.alloc_simple_type(data);
Ok(TypeKey::Simple(key))
}
TypeFrameResult::Complex(complex) => {
let open_content = match &complex.content {
ComplexContentResult::Complex(def) => def.open_content.clone(),
_ => None,
};
#[cfg(feature = "xsd11")]
let assertions = match &complex.content {
ComplexContentResult::Simple(sc) => sc.assertions.clone(),
ComplexContentResult::Complex(cc) => cc.assertions.clone(),
ComplexContentResult::Empty => Vec::new(),
};
let block = resolve_block(complex.block, complex.source.as_ref(), schema_set);
let final_derivation = resolve_final(
complex.final_derivation,
complex.source.as_ref(),
schema_set,
);
let data = ComplexTypeDefData {
name: complex.name, target_namespace,
base_type: complex.base_type.clone(),
derivation_method: complex.derivation_method,
content: complex.content.clone(),
open_content,
attributes: complex.attributes.clone(),
attribute_groups: complex.attribute_groups.clone(),
attribute_wildcard: complex.attribute_wildcard.clone(),
mixed: complex.mixed,
is_abstract: complex.is_abstract,
final_derivation,
block,
default_attributes_apply: complex.default_attributes_apply,
id: complex.id.clone(),
#[cfg(feature = "xsd11")]
assertions,
#[cfg(feature = "xsd11")]
xpath_default_namespace: complex.xpath_default_namespace.clone(),
annotation: complex.annotation.clone(),
source: complex.source.clone(),
resolved_base_type: None,
resolved_attribute_groups: Vec::new(),
resolved_attributes: Vec::new(),
resolved_content_particle_types: Vec::new(),
resolved_content_particle_elements: Vec::new(),
resolved_simple_content_type: None,
redefine_original: None,
};
let key = schema_set.arenas.alloc_complex_type(data);
Ok(TypeKey::Complex(key))
}
}
}
fn update_owner(
schema_set: &mut SchemaSet,
job: &InlineTypeJob,
type_key: TypeKey,
stats: &mut InlineAssemblyStats,
) -> SchemaResult<()> {
match job.owner {
InlineOwner::Element(key) => {
if let Some(elem) = schema_set.arenas.elements.get_mut(key) {
match job.role {
#[cfg(feature = "xsd11")]
InlineRole::AlternativeType(idx) => {
if let Some(alt) = elem.alternatives.get_mut(idx) {
alt.resolved_type = Some(type_key);
}
}
_ => {
elem.resolved_type = Some(type_key);
stats.element_inline_types += 1;
}
}
stats.total_inline_types += 1;
}
}
InlineOwner::Attribute(key) => {
if let Some(attr) = schema_set.arenas.attributes.get_mut(key) {
attr.resolved_type = Some(type_key);
stats.attribute_inline_types += 1;
stats.total_inline_types += 1;
}
}
InlineOwner::SimpleType(key) => {
if let Some(simple) = schema_set.arenas.simple_types.get_mut(key) {
match job.role {
InlineRole::SimpleTypeBase => {
simple.resolved_base_type = Some(type_key);
}
InlineRole::ListItemType => {
simple.resolved_item_type = Some(type_key);
}
InlineRole::UnionMemberType(idx) => {
while simple.resolved_member_types.len() <= idx {
simple.resolved_member_types.push(type_key);
}
simple.resolved_member_types[idx] = type_key;
}
_ => {}
}
stats.simple_type_inline_derivations += 1;
stats.total_inline_types += 1;
}
}
InlineOwner::ComplexType(key) => {
if let Some(complex) = schema_set.arenas.complex_types.get_mut(key) {
match job.role {
InlineRole::ComplexTypeBase => {
complex.resolved_base_type = Some(type_key);
stats.complex_type_inline_derivations += 1;
}
InlineRole::ComplexTypeAttribute(idx) => {
while complex.resolved_attributes.len() <= idx {
complex.resolved_attributes.push(ResolvedAttributeUse {
resolved_type: None,
resolved_ref: None,
});
}
complex.resolved_attributes[idx].resolved_type = Some(type_key);
stats.complex_type_attribute_inline_types += 1;
}
InlineRole::ContentParticleType(idx) => {
while complex.resolved_content_particle_types.len() <= idx {
complex.resolved_content_particle_types.push(None);
}
complex.resolved_content_particle_types[idx] = Some(type_key);
stats.complex_type_inline_derivations += 1;
}
InlineRole::SimpleContentInlineType => {
complex.resolved_simple_content_type = Some(type_key);
stats.complex_type_inline_derivations += 1;
}
_ => {}
}
stats.total_inline_types += 1;
}
}
InlineOwner::ModelGroup(key) => {
if let Some(group) = schema_set.arenas.model_groups.get_mut(key) {
if let InlineRole::ModelGroupParticle(flat_idx) = job.role {
while group.resolved_particle_types.len() <= flat_idx {
group.resolved_particle_types.push(None);
}
group.resolved_particle_types[flat_idx] = Some(type_key);
stats.model_group_inline_types += 1;
stats.total_inline_types += 1;
}
}
}
InlineOwner::AttributeGroup(key) => {
if let Some(group) = schema_set.arenas.attribute_groups.get_mut(key) {
if let InlineRole::AttributeGroupAttribute(idx) = job.role {
while group.resolved_attributes.len() <= idx {
group.resolved_attributes.push(ResolvedAttributeUse {
resolved_type: None,
resolved_ref: None,
});
}
group.resolved_attributes[idx].resolved_type = Some(type_key);
stats.attribute_group_inline_types += 1;
stats.total_inline_types += 1;
}
}
}
}
Ok(())
}
fn collect_nested_inline_types(
schema_set: &SchemaSet,
type_key: TypeKey,
target_namespace: Option<NameId>,
jobs: &mut Vec<InlineTypeJob>,
) {
match type_key {
TypeKey::Simple(key) => {
if let Some(simple) = schema_set.arenas.simple_types.get(key) {
if let Some(TypeRefResult::Inline(inline_type)) = &simple.base_type {
jobs.push(InlineTypeJob {
owner: InlineOwner::SimpleType(key),
role: InlineRole::SimpleTypeBase,
type_frame: (**inline_type).clone(),
target_namespace,
});
}
if let Some(TypeRefResult::Inline(inline_type)) = &simple.item_type {
jobs.push(InlineTypeJob {
owner: InlineOwner::SimpleType(key),
role: InlineRole::ListItemType,
type_frame: (**inline_type).clone(),
target_namespace,
});
}
for (idx, member) in simple.member_types.iter().enumerate() {
if let TypeRefResult::Inline(inline_type) = member {
jobs.push(InlineTypeJob {
owner: InlineOwner::SimpleType(key),
role: InlineRole::UnionMemberType(idx),
type_frame: (**inline_type).clone(),
target_namespace,
});
}
}
}
}
TypeKey::Complex(key) => {
if let Some(complex) = schema_set.arenas.complex_types.get(key) {
if let Some(TypeRefResult::Inline(inline_type)) = &complex.base_type {
jobs.push(InlineTypeJob {
owner: InlineOwner::ComplexType(key),
role: InlineRole::ComplexTypeBase,
type_frame: (**inline_type).clone(),
target_namespace,
});
}
for (idx, attr_use) in complex.attributes.iter().enumerate() {
if let Some(inline_type) = &attr_use.attribute.inline_type {
jobs.push(InlineTypeJob {
owner: InlineOwner::ComplexType(key),
role: InlineRole::ComplexTypeAttribute(idx),
type_frame: TypeFrameResult::Simple(Box::new((**inline_type).clone())),
target_namespace,
});
}
}
collect_content_inline_types(&complex.content, key, target_namespace, jobs);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::arenas::ElementDeclData;
use crate::parser::frames::{SimpleTypeResult, SimpleTypeVariety};
use crate::schema::model::DerivationSet;
use crate::types::facets::FacetSet;
fn create_simple_type_frame(variety: SimpleTypeVariety) -> TypeFrameResult {
TypeFrameResult::Simple(Box::new(SimpleTypeResult {
name: None,
variety,
base_type: None,
item_type: None,
member_types: Vec::new(),
facets: FacetSet::new(),
final_derivation: None,
id: None,
derivation_id: None,
annotation: None,
source: None,
}))
}
#[test]
fn test_assemble_inline_types_empty_schema() {
let mut schema_set = SchemaSet::new();
let result = assemble_inline_types(&mut schema_set);
assert!(result.is_ok());
let stats = result.unwrap();
assert_eq!(stats.total_inline_types, 0);
}
#[test]
fn test_element_with_inline_simple_type() {
let mut schema_set = SchemaSet::new();
let elem_name = schema_set.name_table.add("testElement");
let inline_type = Box::new(create_simple_type_frame(SimpleTypeVariety::Atomic));
let elem_data = ElementDeclData {
name: Some(elem_name),
target_namespace: None,
ref_name: None,
type_ref: None,
inline_type: Some(inline_type),
substitution_group: Vec::new(),
default_value: None,
fixed_value: None,
nillable: false,
is_abstract: false,
min_occurs: 1,
max_occurs: Some(1),
block: DerivationSet::empty(),
final_derivation: DerivationSet::empty(),
form: None,
id: None,
alternatives: Vec::new(),
identity_constraints: Vec::new(),
pending_ic_refs: vec![],
annotation: None,
source: None,
resolved_type: None,
resolved_ref: None,
resolved_substitution_groups: Vec::new(),
deferred_type_error: None,
};
let elem_key = schema_set.arenas.alloc_element(elem_data);
let result = assemble_inline_types(&mut schema_set);
assert!(result.is_ok());
let stats = result.unwrap();
assert_eq!(stats.element_inline_types, 1);
assert_eq!(stats.total_inline_types, 1);
let elem = schema_set.arenas.elements.get(elem_key).unwrap();
assert!(elem.resolved_type.is_some());
assert!(matches!(elem.resolved_type, Some(TypeKey::Simple(_))));
assert!(elem.inline_type.is_some());
}
#[test]
fn test_simple_type_with_inline_base() {
let mut schema_set = SchemaSet::new();
let type_name = schema_set.name_table.add("testType");
let inline_base = Box::new(create_simple_type_frame(SimpleTypeVariety::Atomic));
let type_data = SimpleTypeDefData {
name: Some(type_name),
target_namespace: None,
variety: SimpleTypeVariety::Atomic,
base_type: Some(TypeRefResult::Inline(inline_base)),
item_type: None,
member_types: Vec::new(),
facets: FacetSet::new(),
final_derivation: DerivationSet::empty(),
id: None,
derivation_id: None,
annotation: None,
source: None,
resolved_base_type: None,
resolved_item_type: None,
resolved_member_types: Vec::new(),
redefine_original: None,
deferred_item_type_error: None,
};
let type_key = schema_set.arenas.alloc_simple_type(type_data);
let result = assemble_inline_types(&mut schema_set);
assert!(result.is_ok());
let stats = result.unwrap();
assert_eq!(stats.simple_type_inline_derivations, 1);
let simple = schema_set.arenas.simple_types.get(type_key).unwrap();
assert!(simple.resolved_base_type.is_some());
assert!(matches!(simple.base_type, Some(TypeRefResult::Inline(_))));
}
#[test]
fn test_list_type_with_inline_item() {
let mut schema_set = SchemaSet::new();
let type_name = schema_set.name_table.add("listType");
let inline_item = Box::new(create_simple_type_frame(SimpleTypeVariety::Atomic));
let type_data = SimpleTypeDefData {
name: Some(type_name),
target_namespace: None,
variety: SimpleTypeVariety::List,
base_type: None,
item_type: Some(TypeRefResult::Inline(inline_item)),
member_types: Vec::new(),
facets: FacetSet::new(),
final_derivation: DerivationSet::empty(),
id: None,
derivation_id: None,
annotation: None,
source: None,
resolved_base_type: None,
resolved_item_type: None,
resolved_member_types: Vec::new(),
redefine_original: None,
deferred_item_type_error: None,
};
let type_key = schema_set.arenas.alloc_simple_type(type_data);
let result = assemble_inline_types(&mut schema_set);
assert!(result.is_ok());
let simple = schema_set.arenas.simple_types.get(type_key).unwrap();
assert!(simple.resolved_item_type.is_some());
}
#[test]
fn test_union_type_with_inline_members() {
let mut schema_set = SchemaSet::new();
let type_name = schema_set.name_table.add("unionType");
let inline_member1 = Box::new(create_simple_type_frame(SimpleTypeVariety::Atomic));
let inline_member2 = Box::new(create_simple_type_frame(SimpleTypeVariety::Atomic));
let type_data = SimpleTypeDefData {
name: Some(type_name),
target_namespace: None,
variety: SimpleTypeVariety::Union,
base_type: None,
item_type: None,
member_types: vec![
TypeRefResult::Inline(inline_member1),
TypeRefResult::Inline(inline_member2),
],
facets: FacetSet::new(),
final_derivation: DerivationSet::empty(),
id: None,
derivation_id: None,
annotation: None,
source: None,
resolved_base_type: None,
resolved_item_type: None,
resolved_member_types: Vec::new(),
redefine_original: None,
deferred_item_type_error: None,
};
let type_key = schema_set.arenas.alloc_simple_type(type_data);
let result = assemble_inline_types(&mut schema_set);
assert!(result.is_ok());
let stats = result.unwrap();
assert_eq!(stats.simple_type_inline_derivations, 2);
let simple = schema_set.arenas.simple_types.get(type_key).unwrap();
assert_eq!(simple.resolved_member_types.len(), 2);
}
#[test]
fn test_inline_type_in_model_group_resolved() {
use crate::arenas::ModelGroupData;
use crate::parser::frames::{Compositor, ElementFrameResult};
let mut schema_set = SchemaSet::new();
let elem_name = schema_set.name_table.add("detail");
let inline_type = Box::new(create_simple_type_frame(SimpleTypeVariety::Atomic));
let group_data = ModelGroupData {
name: Some(schema_set.name_table.add("myGroup")),
target_namespace: None,
ref_name: None,
compositor: Some(Compositor::Sequence),
particles: vec![ParticleResult {
term: ParticleTerm::Element(ElementFrameResult {
name: Some(elem_name),
ref_name: None,
target_namespace: None,
type_ref: None,
inline_type: Some(inline_type),
substitution_group: vec![],
default_value: None,
fixed_value: None,
nillable: false,
is_abstract: false,
min_occurs: 1,
max_occurs: Some(1),
block: Default::default(),
final_derivation: Default::default(),
form: None,
id: None,
alternatives: vec![],
identity_constraints: vec![],
identity_constraint_refs: vec![],
annotation: None,
source: None,
}),
min_occurs: 1,
max_occurs: Some(1),
source: None,
}],
min_occurs: 1,
max_occurs: Some(1),
id: None,
annotation: None,
source: None,
resolved_ref: None,
resolved_particles: Vec::new(),
resolved_particle_types: Vec::new(),
resolved_particle_elements: Vec::new(),
redefine_original: None,
redefine_requires_restriction_check: false,
};
let _group_key = schema_set.arenas.alloc_model_group(group_data);
let result = assemble_inline_types(&mut schema_set);
assert!(result.is_ok());
let stats = result.unwrap();
assert_eq!(stats.model_group_inline_types, 1);
assert_eq!(stats.total_inline_types, 1);
let group = schema_set.arenas.model_groups.get(_group_key).unwrap();
assert_eq!(group.resolved_particle_types.len(), 1);
assert!(
group.resolved_particle_types[0].is_some(),
"Expected resolved type at flat index 0"
);
}
#[test]
fn test_inline_type_in_content_particle_resolved() {
use crate::parser::frames::{
ComplexContentDefResult, Compositor, ElementFrameResult, ModelGroupDefResult,
};
let mut schema_set = SchemaSet::new();
let elem_name = schema_set.name_table.add("child");
let inline_type = Box::new(create_simple_type_frame(SimpleTypeVariety::Atomic));
let content_particle = ParticleResult {
term: ParticleTerm::Group(ModelGroupDefResult {
name: None,
ref_name: None,
compositor: Some(Compositor::Sequence),
particles: vec![ParticleResult {
term: ParticleTerm::Element(ElementFrameResult {
name: Some(elem_name),
ref_name: None,
target_namespace: None,
type_ref: None,
inline_type: Some(inline_type),
substitution_group: vec![],
default_value: None,
fixed_value: None,
nillable: false,
is_abstract: false,
min_occurs: 1,
max_occurs: Some(1),
block: Default::default(),
final_derivation: Default::default(),
form: None,
id: None,
alternatives: vec![],
identity_constraints: vec![],
identity_constraint_refs: vec![],
annotation: None,
source: None,
}),
min_occurs: 1,
max_occurs: Some(1),
source: None,
}],
min_occurs: 1,
max_occurs: Some(1),
id: None,
annotation: None,
source: None,
}),
min_occurs: 1,
max_occurs: Some(1),
source: None,
};
let complex_data = ComplexTypeDefData {
name: Some(schema_set.name_table.add("MyComplexType")),
target_namespace: None,
base_type: None,
derivation_method: None,
content: ComplexContentResult::Complex(ComplexContentDefResult {
particle: Some(content_particle),
derivation: crate::parser::frames::DerivationMethod::Restriction,
mixed: false,
mixed_explicit: false,
base_type: None,
open_content: None,
attributes: Vec::new(),
attribute_groups: Vec::new(),
attribute_wildcard: None,
assertions: Vec::new(),
id: None,
derivation_id: None,
source: None,
}),
open_content: None,
attributes: Vec::new(),
attribute_groups: Vec::new(),
attribute_wildcard: None,
mixed: false,
is_abstract: false,
final_derivation: DerivationSet::empty(),
block: DerivationSet::empty(),
default_attributes_apply: true,
id: None,
#[cfg(feature = "xsd11")]
assertions: Vec::new(),
#[cfg(feature = "xsd11")]
xpath_default_namespace: None,
annotation: None,
source: None,
resolved_base_type: None,
resolved_attribute_groups: Vec::new(),
resolved_attributes: Vec::new(),
resolved_content_particle_types: Vec::new(),
resolved_content_particle_elements: Vec::new(),
resolved_simple_content_type: None,
redefine_original: None,
};
let ct_key = schema_set.arenas.alloc_complex_type(complex_data);
let result = assemble_inline_types(&mut schema_set);
assert!(result.is_ok());
let stats = result.unwrap();
assert!(stats.total_inline_types >= 1);
let ct = schema_set.arenas.complex_types.get(ct_key).unwrap();
assert_eq!(ct.resolved_content_particle_types.len(), 1);
assert!(ct.resolved_content_particle_types[0].is_some());
}
#[test]
fn test_nested_inline_type_in_model_group() {
use crate::arenas::ModelGroupData;
use crate::parser::frames::{Compositor, ElementFrameResult, ModelGroupDefResult};
let mut schema_set = SchemaSet::new();
let elem_name = schema_set.name_table.add("nested_elem");
let inline_type = Box::new(create_simple_type_frame(SimpleTypeVariety::Atomic));
let group_data = ModelGroupData {
name: Some(schema_set.name_table.add("G")),
target_namespace: None,
ref_name: None,
compositor: Some(Compositor::Sequence),
particles: vec![ParticleResult {
term: ParticleTerm::Group(ModelGroupDefResult {
name: None,
ref_name: None,
compositor: Some(Compositor::Choice),
particles: vec![ParticleResult {
term: ParticleTerm::Element(ElementFrameResult {
name: Some(elem_name),
ref_name: None,
target_namespace: None,
type_ref: None,
inline_type: Some(inline_type),
substitution_group: vec![],
default_value: None,
fixed_value: None,
nillable: false,
is_abstract: false,
min_occurs: 1,
max_occurs: Some(1),
block: Default::default(),
final_derivation: Default::default(),
form: None,
id: None,
alternatives: vec![],
identity_constraints: vec![],
identity_constraint_refs: vec![],
annotation: None,
source: None,
}),
min_occurs: 1,
max_occurs: Some(1),
source: None,
}],
min_occurs: 1,
max_occurs: Some(1),
id: None,
annotation: None,
source: None,
}),
min_occurs: 1,
max_occurs: Some(1),
source: None,
}],
min_occurs: 1,
max_occurs: Some(1),
id: None,
annotation: None,
source: None,
resolved_ref: None,
resolved_particles: Vec::new(),
resolved_particle_types: Vec::new(),
resolved_particle_elements: Vec::new(),
redefine_original: None,
redefine_requires_restriction_check: false,
};
let group_key = schema_set.arenas.alloc_model_group(group_data);
let result = assemble_inline_types(&mut schema_set);
assert!(result.is_ok());
let stats = result.unwrap();
assert_eq!(stats.model_group_inline_types, 1);
assert_eq!(stats.total_inline_types, 1);
let group = schema_set.arenas.model_groups.get(group_key).unwrap();
assert_eq!(group.resolved_particle_types.len(), 1);
assert!(
group.resolved_particle_types[0].is_some(),
"Expected resolved type for nested element at flat index 0"
);
assert!(matches!(
group.resolved_particle_types[0],
Some(TypeKey::Simple(_))
));
}
}