pub mod annotation;
pub mod composition;
#[cfg(feature = "xsd11")]
pub mod cta;
pub mod decl;
pub mod dependencies;
pub mod derivation;
#[cfg(feature = "xsd11")]
pub mod edc;
pub mod group;
pub mod inline;
pub mod model;
#[cfg(feature = "xsd11")]
pub mod override_dir;
pub mod redefine;
pub mod resolver;
pub mod wildcard;
pub use model::{
DefaultOpenContent, DerivationSet, FormChoice, ImportDirective, IncludeDirective,
NamespaceTable, OpenContentMode, OverrideDirective, RedefineDirective, SchemaDocument,
SchemaSet, XsdVersion,
};
pub use decl::{
AttributeDecl, DeclarationScope, ElementDecl, FormKind, NotationDecl, TypeReference,
ValueConstraint,
};
pub use group::{AttributeGroupDef, AttributeGroupRef, ModelGroupDef, ModelGroupRef, Occurrence};
pub use wildcard::{ElementWildcard, NamespaceConstraint, ProcessContents};
pub use composition::{
ComponentIdentity, ComponentKey, ComponentKind, ComponentOrigin, CompositionAction,
CompositionEdge, CompositionEdgeKind, DocumentComponentIndex, EffectiveComponent,
};
pub use annotation::{
Annotation, AnnotationItem, AppInfoElement, DocumentationElement, ForeignAttribute, XmlFragment,
};
pub use resolver::{
finalize_pending_ic_refs, resolve_all_references, ReferenceResolver, ResolutionStats,
ResolvedReferences,
};
pub use inline::{
allocate_content_particle_elements, allocate_model_group_particle_elements,
assemble_inline_types, InlineAssemblyStats,
};
pub use dependencies::{build_dependency_graph, DependencyGraph, DependencyStats};
#[cfg(feature = "xsd11")]
pub use cta::validate_cta_substitutability;
#[cfg(feature = "xsd11")]
pub use cta::validate_cta_xpath;
#[cfg(feature = "xsd11")]
pub use derivation::validate_element_type_alternatives;
#[cfg(feature = "xsd11")]
pub use derivation::validate_local_element_type_table_consistency;
#[cfg(feature = "xsd11")]
pub use derivation::validate_restriction_local_element_type_table_consistency;
#[cfg(feature = "xsd11")]
pub use derivation::validate_wildcard_disallowed_names;
#[cfg(feature = "xsd11")]
pub use derivation::validate_wildcard_element_type_table_consistency;
pub use derivation::{
validate_all_derivations, validate_attribute_id_constraints,
validate_attribute_value_constraints, validate_complex_type_attribute_uniqueness,
validate_element_value_constraints, validate_local_decl_target_namespace,
validate_no_xsi_attribute_declarations, validate_substitution_group_element_consistency,
validate_xsd10_annotation_source_anyuri, DerivationStats as DerivationValidationStats,
};
pub use redefine::apply_redefine;
#[cfg(feature = "xsd11")]
pub use override_dir::apply_override;
use crate::error::{SchemaError, SchemaResult};
pub fn compile_all_patterns(schema_set: &mut SchemaSet) -> SchemaResult<()> {
let xsd_version = schema_set.xsd_version;
let regex_compat = schema_set.regex_compatibility;
let keys: Vec<_> = schema_set.arenas.simple_types.keys().collect();
for key in keys {
let type_def = &mut schema_set.arenas.simple_types[key];
if let Err(facet_err) = type_def.facets.compile_patterns(xsd_version, regex_compat) {
let source = type_def.source.clone();
let location = source
.as_ref()
.and_then(|src| schema_set.source_maps.locate(src));
return Err(SchemaError::structural(
"pattern-valid",
facet_err.to_string(),
location,
));
}
}
Ok(())
}
pub fn apply_redefine_override(schema_set: &mut SchemaSet) -> SchemaResult<()> {
validate_redefine_originals_exist(schema_set)?;
collect_declared_components(schema_set);
let redefines = topologically_ordered_redefines(schema_set);
for redefine in redefines {
apply_redefine(schema_set, &redefine)?;
}
#[cfg(feature = "xsd11")]
override_dir::validate_override_directives(schema_set)?;
#[cfg(feature = "xsd11")]
{
let overrides = topologically_ordered_overrides(schema_set);
for override_dir in overrides {
apply_override(schema_set, &override_dir)?;
}
}
Ok(())
}
fn validate_redefine_originals_exist(schema_set: &SchemaSet) -> SchemaResult<()> {
use crate::ids::{DocumentId, NameId};
use crate::schema::composition::ComponentKind;
use std::collections::HashSet;
fn lookup_via_redefine_chain(
schema_set: &SchemaSet,
start_doc: DocumentId,
kind: ComponentKind,
namespace: Option<NameId>,
name: NameId,
visiting: &mut HashSet<DocumentId>,
) -> bool {
if !visiting.insert(start_doc) {
return false;
}
let found = (|| {
let Some(doc) = schema_set.documents.get(start_doc as usize) else {
return false;
};
let direct = match kind {
ComponentKind::SimpleType => doc
.component_index
.lookup_simple_type(namespace, name)
.is_some(),
ComponentKind::ComplexType => doc
.component_index
.lookup_complex_type(namespace, name)
.is_some(),
ComponentKind::ModelGroup => doc
.component_index
.lookup_model_group(namespace, name)
.is_some(),
ComponentKind::AttributeGroup => doc
.component_index
.lookup_attribute_group(namespace, name)
.is_some(),
_ => false,
};
if direct {
return true;
}
for r in &doc.redefines {
if let Some(target) = r.resolved_doc_id {
if lookup_via_redefine_chain(
schema_set, target, kind, namespace, name, visiting,
) {
return true;
}
}
}
false
})();
visiting.remove(&start_doc);
found
}
let make_err = |schema_set: &SchemaSet,
redefining_doc: &crate::schema::model::SchemaDocument,
directive: &crate::schema::model::RedefineDirective,
kind_label: &str,
name: NameId| {
let target_label = directive
.resolved_doc_id
.and_then(|id| schema_set.documents.get(id as usize))
.map(|d| d.base_uri.as_str())
.unwrap_or(directive.schema_location.as_str());
let location = directive
.source
.as_ref()
.and_then(|s| schema_set.source_maps.locate(s));
SchemaError::structural(
"src-redefine",
format!(
"Original {} '{}' not found at parse time in '{}' for redefinition \
from '{}' (the redefine chain has no non-cyclic anchor)",
kind_label,
schema_set.name_table.resolve(name),
target_label,
redefining_doc.base_uri,
),
location,
)
};
for doc in &schema_set.documents {
for redefine in &doc.redefines {
let Some(target_doc_id) = redefine.resolved_doc_id else {
continue;
};
let simples = redefine.simple_types.iter().filter_map(|&k| {
let st = schema_set.arenas.simple_types.get(k)?;
Some((
ComponentKind::SimpleType,
st.target_namespace,
st.name?,
"simple type",
))
});
let complexes = redefine.complex_types.iter().filter_map(|&k| {
let ct = schema_set.arenas.complex_types.get(k)?;
Some((
ComponentKind::ComplexType,
ct.target_namespace,
ct.name?,
"complex type",
))
});
let groups = redefine.groups.iter().filter_map(|&k| {
let g = schema_set.arenas.model_groups.get(k)?;
Some((
ComponentKind::ModelGroup,
g.target_namespace,
g.name?,
"model group",
))
});
let attr_groups = redefine.attribute_groups.iter().filter_map(|&k| {
let ag = schema_set.arenas.attribute_groups.get(k)?;
Some((
ComponentKind::AttributeGroup,
ag.target_namespace,
ag.name?,
"attribute group",
))
});
let mut visiting: HashSet<DocumentId> = HashSet::with_capacity(4);
visiting.insert(doc.id);
for (kind, namespace, name, label) in
simples.chain(complexes).chain(groups).chain(attr_groups)
{
if !lookup_via_redefine_chain(
schema_set,
target_doc_id,
kind,
namespace,
name,
&mut visiting,
) {
return Err(make_err(schema_set, doc, redefine, label, name));
}
}
}
}
Ok(())
}
fn topologically_ordered_redefines(
schema_set: &SchemaSet,
) -> Vec<crate::schema::model::RedefineDirective> {
use crate::ids::DocumentId;
use crate::schema::model::{RedefineDirective, SchemaDocument};
use std::collections::{HashMap, HashSet};
fn depth(
doc_id: DocumentId,
docs: &[SchemaDocument],
cache: &mut HashMap<DocumentId, usize>,
visiting: &mut HashSet<DocumentId>,
) -> usize {
if let Some(&d) = cache.get(&doc_id) {
return d;
}
if !visiting.insert(doc_id) {
return 0;
}
let d = docs
.iter()
.find(|d| d.id == doc_id)
.map(|doc| {
let mut max_dep = 0usize;
for r in &doc.redefines {
if let Some(target) = r.resolved_doc_id {
let t = depth(target, docs, cache, visiting) + 1;
if t > max_dep {
max_dep = t;
}
}
}
for inc in &doc.includes {
if let Some(target) = inc.resolved_doc_id {
let t = depth(target, docs, cache, visiting) + 1;
if t > max_dep {
max_dep = t;
}
}
}
max_dep
})
.unwrap_or(0);
visiting.remove(&doc_id);
cache.insert(doc_id, d);
d
}
let mut cache: HashMap<DocumentId, usize> = HashMap::new();
for doc in &schema_set.documents {
depth(
doc.id,
&schema_set.documents,
&mut cache,
&mut HashSet::new(),
);
}
let mut tagged: Vec<(usize, RedefineDirective)> = schema_set
.documents
.iter()
.flat_map(|doc| {
let d = cache.get(&doc.id).copied().unwrap_or(0);
doc.redefines.iter().cloned().map(move |r| (d, r))
})
.collect();
tagged.sort_by_key(|(d, _)| *d);
tagged.into_iter().map(|(_, r)| r).collect()
}
#[cfg(feature = "xsd11")]
fn topologically_ordered_overrides(
schema_set: &SchemaSet,
) -> Vec<crate::schema::model::OverrideDirective> {
use crate::ids::DocumentId;
use crate::schema::model::{OverrideDirective, SchemaDocument};
use std::collections::{HashMap, HashSet};
fn depth(
doc_id: DocumentId,
docs: &[SchemaDocument],
cache: &mut HashMap<DocumentId, usize>,
visiting: &mut HashSet<DocumentId>,
) -> usize {
if let Some(&d) = cache.get(&doc_id) {
return d;
}
if !visiting.insert(doc_id) {
return 0;
}
let d = docs
.iter()
.find(|d| d.id == doc_id)
.map(|doc| {
let mut max_dep = 0usize;
for o in &doc.overrides {
if let Some(target) = o.resolved_doc_id {
let t = depth(target, docs, cache, visiting) + 1;
if t > max_dep {
max_dep = t;
}
}
}
for inc in &doc.includes {
if let Some(target) = inc.resolved_doc_id {
let t = depth(target, docs, cache, visiting) + 1;
if t > max_dep {
max_dep = t;
}
}
}
max_dep
})
.unwrap_or(0);
visiting.remove(&doc_id);
cache.insert(doc_id, d);
d
}
let mut cache: HashMap<DocumentId, usize> = HashMap::new();
for doc in &schema_set.documents {
depth(
doc.id,
&schema_set.documents,
&mut cache,
&mut HashSet::new(),
);
}
let mut tagged: Vec<(usize, OverrideDirective)> = schema_set
.documents
.iter()
.flat_map(|doc| {
let d = cache.get(&doc.id).copied().unwrap_or(0);
doc.overrides.iter().cloned().map(move |o| (d, o))
})
.collect();
tagged.sort_by_key(|(d, _)| *d);
tagged.into_iter().map(|(_, o)| o).collect()
}
fn collect_declared_components(schema_set: &mut SchemaSet) {
use crate::ids::DocumentId;
use crate::schema::composition::CompositionEdgeKind;
use std::collections::HashMap;
let mut included_from: HashMap<DocumentId, DocumentId> = HashMap::new();
for edge in &schema_set.composition_edges {
if edge.kind == CompositionEdgeKind::Include {
if let Some(target) = edge.target_doc {
included_from.entry(target).or_insert(edge.source_doc);
}
}
}
let mut effective: HashMap<ComponentIdentity, EffectiveComponent> = HashMap::new();
for doc in &schema_set.documents {
for (&identity, &key) in doc.component_index.iter() {
let origin = ComponentOrigin {
owner_doc: Some(doc.id),
identity,
};
let action = if let Some(&from_doc) = included_from.get(&doc.id) {
CompositionAction::Included { from_doc }
} else {
CompositionAction::Declared
};
effective.insert(
identity,
EffectiveComponent {
key,
origin,
action,
},
);
}
}
schema_set.effective_components = effective;
}