Class: Lutaml::Xsd::SchemaRepository
- Inherits:
-
Model::Serializable
- Object
- Model::Serializable
- Lutaml::Xsd::SchemaRepository
- Defined in:
- lib/lutaml/xsd/schema_repository.rb,
lib/lutaml/xsd/schema_repository/type_index.rb,
lib/lutaml/xsd/schema_repository/namespace_registry.rb,
lib/lutaml/xsd/schema_repository/qualified_name_parser.rb
Overview
A fully resolved, validated, searchable collection of XSD schemas Provides namespace-aware type resolution across multiple schemas
Defined Under Namespace
Classes: NamespaceRegistry, QualifiedNameParser, TypeIndex
Instance Attribute Summary collapse
-
#lazy_load ⇒ Object
readonly
Internal state (not serialized).
Class Method Summary collapse
-
.from_file(path) ⇒ SchemaRepository
Auto-detect and load from XSD, LXR, or YAML.
-
.from_file_cached(source_path, lxr_path: nil) ⇒ SchemaRepository
Smart caching: only rebuild when source is newer than cache.
-
.from_package(zip_path) ⇒ SchemaRepository
Load repository from a ZIP package.
-
.from_yaml_file(yaml_path) ⇒ SchemaRepository
Load repository configuration from a YAML file.
-
.validate_package(zip_path) ⇒ SchemaRepositoryPackage::ValidationResult
Validate a schema repository package.
Instance Method Summary collapse
-
#add_schema_file(file_path) ⇒ void
Add a schema file to the repository.
-
#add_schema_files(file_paths) ⇒ void
Add multiple schema files to the repository.
-
#add_schema_location_mapping(mapping) ⇒ void
Add a schema location mapping for resolving import/include paths.
-
#all_namespaces ⇒ Array<String>
Get all registered namespace URIs.
-
#all_schemas ⇒ Hash
Get all processed schemas (public accessor for validators/analyzers).
-
#all_type_names(namespace: nil, category: nil) ⇒ Array<String>
List all type names.
-
#analyze_coverage(entry_types: []) ⇒ CoverageReport
Analyze coverage based on entry point types.
-
#analyze_type_hierarchy(qualified_name, depth: 10) ⇒ Hash?
Analyze type inheritance hierarchy.
-
#apply_namespace_remapping_to_schemas(schemas, remappings) ⇒ Hash
Apply namespace remapping to schemas.
-
#base_packages ⇒ Object
Override base_packages getter to ensure array.
-
#base_packages=(value) ⇒ Object
Override base_packages setter to handle mixed types.
-
#classify_schemas ⇒ Hash
Classify schemas by role and resolution status.
-
#configure_namespace(prefix:, uri:) ⇒ self
Configure a single namespace prefix mapping.
-
#configure_namespaces(mappings) ⇒ self
Configure multiple namespace prefix mappings.
-
#configure_schema_location_mappings(mappings) ⇒ self
Configure schema location mappings for imports/includes.
-
#elements_by_namespace(namespace_uri: nil) ⇒ Hash{String => Array<Hash>}
Get all elements organized by namespace Returns hash: { namespace_uri => [type, minOccurs, maxOccurs, documentation] }.
-
#export_statistics(format: :yaml) ⇒ String
Export statistics in different formats.
-
#find_attribute(qualified_name) ⇒ Attribute?
Find an attribute definition by qualified name Searches across all schemas in the repository.
-
#find_attribute_group(qualified_name) ⇒ AttributeGroup?
Find an attribute group definition by qualified name Searches across all schemas in the repository.
-
#find_element(qualified_name) ⇒ Element?
Find an element definition by qualified name Searches across all schemas in the repository.
-
#find_group(qualified_name) ⇒ Group?
Find a group definition by qualified name Searches across all schemas in the repository.
-
#find_type(qname) ⇒ TypeResolutionResult
Resolve a qualified type name to its definition.
-
#initialize(**attributes) ⇒ SchemaRepository
constructor
A new instance of SchemaRepository.
-
#load_base_packages_with_conflict_detection(glob_mappings) ⇒ Object
Load packages with conflict detection and resolution.
-
#load_package_with_filtering(package_source, _glob_mappings) ⇒ Object
Load a single package with schema filtering.
-
#namespace_prefix_details ⇒ Array<NamespacePrefixInfo>
Get detailed namespace prefix information.
-
#namespace_summary ⇒ Array<Hash>
Namespace summary.
-
#namespace_to_prefix(namespace_uri) ⇒ String?
Get the namespace prefix for a URI.
-
#needs_parsing? ⇒ Boolean
Check if repository needs parsing Used by demo scripts to determine if parse() should be called.
-
#normalize_base_packages_to_configs ⇒ Array<BasePackageConfig>
Normalize base_packages to BasePackageConfig objects Handles both legacy string format and new hash/config format.
-
#parse(schema_locations: {}, lazy_load: true, verbose: false) ⇒ self
Parse XSD schemas from configured files and base packages.
-
#parse_qualified_name(qualified_name) ⇒ Hash?
Parse a qualified name into its components.
-
#remap_namespace_prefixes(changes) ⇒ SchemaRepository
Remap namespace prefixes.
-
#resolve(verbose: false) ⇒ self
Force full resolution of all imports/includes and build indexes.
-
#schemas ⇒ Hash
Get all schemas (alias for compatibility).
-
#statistics ⇒ Hash
Get repository statistics.
-
#supports_conflict_detection? ⇒ Boolean
Check if base_packages contains configuration objects.
-
#to_package(output_path, xsd_mode: :include_all, resolution_mode: :resolved, serialization_format: :marshal, metadata: {}) ⇒ SchemaRepositoryPackage
Export repository as a ZIP package with schemas and metadata.
-
#type_exists?(qualified_name) ⇒ Boolean
Quick type existence check.
-
#validate(strict: false) ⇒ Array<String>
Validate the repository.
-
#validate_xsd_spec(version: "1.0") ⇒ SpecComplianceReport
Validate XSD specification compliance.
Constructor Details
#initialize(**attributes) ⇒ SchemaRepository
Returns a new instance of SchemaRepository.
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 41 def initialize(**attributes) # Initialize internal state first @parsed_schemas = {} @namespace_registry = NamespaceRegistry.new @type_index = TypeIndex.new @lazy_load = true @resolved = false @validated = false @verbose = false # Call super to set attributes from Lutaml::Model::Serializable super # Register namespace mappings AFTER super sets the attributes # This ensures they're available immediately when loading from packages return unless namespace_mappings && !namespace_mappings.empty? namespace_mappings.each do |mapping| @namespace_registry.register(mapping.prefix, mapping.uri) end end |
Instance Attribute Details
#lazy_load ⇒ Object (readonly)
Internal state (not serialized)
39 40 41 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 39 def lazy_load @lazy_load end |
Class Method Details
.from_file(path) ⇒ SchemaRepository
Auto-detect and load from XSD, LXR, or YAML
751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 751 def self.from_file(path) # Check file exists first unless File.exist?(path) raise Errno::ENOENT, "No such file or directory - #{path}" end case File.extname(path).downcase when ".lxr" repo = from_package(path) # Ensure loaded repository is resolved repo.resolve unless repo.instance_variable_get(:@resolved) repo when ".xsd" repo = new repo.instance_variable_set(:@files, [File.(path)]) repo.parse.resolve repo when ".yml", ".yaml" repo = from_yaml_file(path) # Parse and resolve if needed repo.parse.resolve if repo.needs_parsing? repo else raise ConfigurationError, "Unsupported file type: #{path}. Expected .xsd, .lxr, .yml, or .yaml" end end |
.from_file_cached(source_path, lxr_path: nil) ⇒ SchemaRepository
Smart caching: only rebuild when source is newer than cache
784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 784 def self.from_file_cached(source_path, lxr_path: nil) lxr_path ||= source_path.sub(/\.(xsd|ya?ml)$/, ".lxr") # Check if cache exists and is fresh if File.exist?(lxr_path) && File.mtime(lxr_path) >= File.mtime(source_path) # Use from_file to ensure proper resolution from_file(lxr_path) else # Cache missing or stale, rebuild repo = from_file(source_path) # Create cache package repo.to_package( lxr_path, xsd_mode: :include_all, resolution_mode: :resolved, serialization_format: :marshal, ) repo end end |
.from_package(zip_path) ⇒ SchemaRepository
Load repository from a ZIP package
693 694 695 696 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 693 def self.from_package(zip_path) package = SchemaRepositoryPackage.new(zip_path) package.load_repository end |
.from_yaml_file(yaml_path) ⇒ SchemaRepository
Load repository configuration from a YAML file
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 701 def self.from_yaml_file(yaml_path) yaml_content = File.read(yaml_path) base_dir = File.dirname(yaml_path) # Use Lutaml::Model's from_yaml to deserialize repository = from_yaml(yaml_content) # Resolve relative paths in files attribute if repository.files repository.instance_variable_set( :@files, repository.files.map do |file| if File.absolute_path?(file) file else File.(file, base_dir) end end, ) end # Resolve relative paths in base_packages attribute if repository.base_packages repository.instance_variable_set( :@base_packages, repository.base_packages.map do |pkg| pkg_path = pkg.package unless File.absolute_path?(pkg_path) = File.(pkg_path, base_dir) pkg.package = end pkg end, ) end # Resolve relative paths in schema_location_mappings repository.schema_location_mappings&.each do |mapping| unless File.absolute_path?(mapping.to) mapping.to = File.(mapping.to, base_dir) end end repository end |
.validate_package(zip_path) ⇒ SchemaRepositoryPackage::ValidationResult
Validate a schema repository package
685 686 687 688 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 685 def self.validate_package(zip_path) package = SchemaRepositoryPackage.new(zip_path) package.validate end |
Instance Method Details
#add_schema_file(file_path) ⇒ void
This method returns an undefined value.
Add a schema file to the repository
644 645 646 647 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 644 def add_schema_file(file_path) @files ||= [] @files << file_path unless @files.include?(file_path) end |
#add_schema_files(file_paths) ⇒ void
This method returns an undefined value.
Add multiple schema files to the repository
652 653 654 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 652 def add_schema_files(file_paths) file_paths.each { |fp| add_schema_file(fp) } end |
#add_schema_location_mapping(mapping) ⇒ void
This method returns an undefined value.
Add a schema location mapping for resolving import/include paths
659 660 661 662 663 664 665 666 667 668 669 670 671 672 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 659 def add_schema_location_mapping(mapping) @schema_location_mappings ||= [] mapping_obj = if mapping.is_a?(SchemaLocationMapping) mapping elsif mapping.is_a?(Hash) SchemaLocationMapping.from_hash(mapping) else raise ArgumentError, "Expected SchemaLocationMapping or Hash, got #{mapping.class}" end @schema_location_mappings << mapping_obj unless @schema_location_mappings.any? do |m| m.from == mapping_obj.from end end |
#all_namespaces ⇒ Array<String>
Get all registered namespace URIs
442 443 444 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 442 def all_namespaces @namespace_registry.all_uris end |
#all_schemas ⇒ Hash
Get all processed schemas (public accessor for validators/analyzers)
566 567 568 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 566 def all_schemas get_all_processed_schemas end |
#all_type_names(namespace: nil, category: nil) ⇒ Array<String>
List all type names
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 457 def all_type_names(namespace: nil, category: nil) types = [] @type_index.all.each_value do |type_info| # Filter by namespace if specified next if namespace && type_info[:namespace] != namespace # Filter by category if specified next if category && type_info[:type] != category # Build qualified name ns = type_info[:namespace] name = type_info[:definition]&.name next unless name prefix = namespace_to_prefix(ns) qualified_name = prefix ? "#{prefix}:#{name}" : name types << qualified_name end types.sort end |
#analyze_coverage(entry_types: []) ⇒ CoverageReport
Analyze coverage based on entry point types
549 550 551 552 553 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 549 def analyze_coverage(entry_types: []) require_relative "coverage_analyzer" analyzer = CoverageAnalyzer.new(self) analyzer.analyze(entry_types: entry_types) end |
#analyze_type_hierarchy(qualified_name, depth: 10) ⇒ Hash?
Analyze type inheritance hierarchy
540 541 542 543 544 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 540 def analyze_type_hierarchy(qualified_name, depth: 10) require_relative "type_hierarchy_analyzer" analyzer = TypeHierarchyAnalyzer.new(self) analyzer.analyze(qualified_name, depth: depth) end |
#apply_namespace_remapping_to_schemas(schemas, remappings) ⇒ Hash
Apply namespace remapping to schemas
940 941 942 943 944 945 946 947 948 949 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 940 def apply_namespace_remapping_to_schemas(schemas, remappings) uri_mappings = {} remappings.each { |remap| uri_mappings[remap.from_uri] = remap.to_uri } # This is a simplified version - in practice, you'd need to # deeply transform all namespace references in the schema objects # For now, just return the schemas as-is since remapping is # handled during conflict detection schemas end |
#base_packages ⇒ Object
Override base_packages getter to ensure array
34 35 36 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 34 def base_packages @base_packages || [] end |
#base_packages=(value) ⇒ Object
Override base_packages setter to handle mixed types
29 30 31 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 29 def base_packages=(value) @base_packages = value end |
#classify_schemas ⇒ Hash
Classify schemas by role and resolution status
434 435 436 437 438 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 434 def classify_schemas require_relative "schema_classifier" classifier = SchemaClassifier.new(self) classifier.classify end |
#configure_namespace(prefix:, uri:) ⇒ self
Configure a single namespace prefix mapping
213 214 215 216 217 218 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 213 def configure_namespace(prefix:, uri:) @namespace_mappings ||= [] @namespace_mappings << NamespaceMapping.new(prefix: prefix, uri: uri) @namespace_registry.register(prefix, uri) self end |
#configure_namespaces(mappings) ⇒ self
Configure multiple namespace prefix mappings
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 223 def configure_namespaces(mappings) case mappings when Hash mappings.each do |prefix, uri| configure_namespace(prefix: prefix, uri: uri) end when Array mappings.each do |mapping| if mapping.is_a?(NamespaceMapping) configure_namespace(prefix: mapping.prefix, uri: mapping.uri) elsif mapping.is_a?(Hash) prefix = mapping[:prefix] || mapping["prefix"] uri = mapping[:uri] || mapping["uri"] configure_namespace(prefix: prefix, uri: uri) end end end self end |
#configure_schema_location_mappings(mappings) ⇒ self
Configure schema location mappings for imports/includes
677 678 679 680 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 677 def configure_schema_location_mappings(mappings) mappings.each { |m| add_schema_location_mapping(m) } self end |
#elements_by_namespace(namespace_uri: nil) ⇒ Hash{String => Array<Hash>}
Get all elements organized by namespace Returns hash: { namespace_uri => [type, minOccurs, maxOccurs, documentation] }
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 580 def elements_by_namespace(namespace_uri: nil) results = {} get_all_processed_schemas.each_value do |schema| ns = schema.target_namespace next if namespace_uri && ns != namespace_uri results[ns] ||= [] (schema.element || []).each do |elem| results[ns] << { name: elem.name, qualified_name: "#{namespace_to_prefix(ns)}:#{elem.name}", type: elem.type || "(inline complex type)", min_occurs: elem.min_occurs || "1", max_occurs: elem.max_occurs || "1", documentation: extract_element_documentation(elem), } end end results end |
#export_statistics(format: :yaml) ⇒ String
Export statistics in different formats
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 483 def export_statistics(format: :yaml) stats = statistics case format when :yaml require "yaml" stats.to_yaml when :json require "json" JSON.pretty_generate(stats) when :text format_statistics_as_text(stats) else raise ArgumentError, "Unsupported format: #{format}" end end |
#find_attribute(qualified_name) ⇒ Attribute?
Find an attribute definition by qualified name Searches across all schemas in the repository
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 311 def find_attribute(qualified_name) # Parse the qualified name parsed = QualifiedNameParser.parse(qualified_name, @namespace_registry) return nil unless parsed namespace_uri = parsed[:namespace] local_name = parsed[:local_name] # Look up attribute in the type index attr_info = @type_index.find_by_namespace_and_name(namespace_uri, local_name) # Return the definition if it's an attribute return unless attr_info && attr_info[:type] == :attribute attr_info[:definition] end |
#find_attribute_group(qualified_name) ⇒ AttributeGroup?
Find an attribute group definition by qualified name Searches across all schemas in the repository
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 388 def find_attribute_group(qualified_name) parsed = parse_qualified_name(qualified_name) return nil unless parsed namespace_uri = parsed[:namespace] local_name = parsed[:local_name] # Get all processed schemas (including those from loaded packages) all_schemas = get_all_processed_schemas all_schemas.each_value do |schema| next unless schema.target_namespace == namespace_uri ag = schema.attribute_group.find { |g| g.name == local_name } return ag if ag end nil end |
#find_element(qualified_name) ⇒ Element?
Find an element definition by qualified name Searches across all schemas in the repository
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 333 def find_element(qualified_name) # Parse the qualified name parsed = parse_qualified_name(qualified_name) return nil unless parsed namespace_uri = parsed[:namespace] local_name = parsed[:local_name] # Get all processed schemas (including those from loaded packages) all_schemas = get_all_processed_schemas # Search all schemas all_schemas.each_value do |schema| # For unprefixed names (namespace_uri is nil), search in all namespaces # For prefixed names, only search in matching namespace next if namespace_uri && schema.target_namespace != namespace_uri # Search in top-level elements elements = schema.element elements = [elements] unless elements.is_a?(Array) elem = elements.compact.find { |e| e.name == local_name } return elem if elem end nil end |
#find_group(qualified_name) ⇒ Group?
Find a group definition by qualified name Searches across all schemas in the repository
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 364 def find_group(qualified_name) parsed = parse_qualified_name(qualified_name) return nil unless parsed namespace_uri = parsed[:namespace] local_name = parsed[:local_name] # Get all processed schemas (including those from loaded packages) all_schemas = get_all_processed_schemas all_schemas.each_value do |schema| next unless schema.target_namespace == namespace_uri grp = schema.group.find { |g| g.name == local_name } return grp if grp end nil end |
#find_type(qname) ⇒ TypeResolutionResult
Resolve a qualified type name to its definition
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 246 def find_type(qname) resolution_path = [qname] # Parse the qualified name parsed = QualifiedNameParser.parse(qname, @namespace_registry) unless parsed return TypeResolutionResult.failure( qname: qname, error_message: "Failed to parse qualified name: #{qname}", resolution_path: resolution_path, ) end namespace = parsed[:namespace] local_name = parsed[:local_name] # Add Clark notation to resolution path clark_notation = QualifiedNameParser.to_clark_notation(parsed) resolution_path << clark_notation if clark_notation != qname # Check if namespace was resolved for prefixed names # For unprefixed names, namespace can be nil and that's valid if parsed[:prefix] && !namespace return TypeResolutionResult.failure( qname: qname, local_name: local_name, error_message: "Namespace prefix '#{parsed[:prefix]}' not registered", resolution_path: resolution_path, ) end # Look up type in index type_info = @type_index.find_by_namespace_and_name(namespace, local_name) if type_info resolution_path << "#{type_info[:schema_file]}##{local_name}" TypeResolutionResult.success( qname: qname, namespace: namespace, local_name: local_name, definition: type_info[:definition], schema_file: type_info[:schema_file], resolution_path: resolution_path, ) else # Provide suggestions for similar types suggestions = @type_index.suggest_similar(namespace, local_name) suggestion_text = suggestions.empty? ? "" : " Did you mean: #{suggestions.join(', ')}?" TypeResolutionResult.failure( qname: qname, namespace: namespace, local_name: local_name, error_message: "Type '#{local_name}' not found in namespace '#{namespace}'.#{suggestion_text}", resolution_path: resolution_path, ) end end |
#load_base_packages_with_conflict_detection(glob_mappings) ⇒ Object
Load packages with conflict detection and resolution
834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 834 def load_base_packages_with_conflict_detection(glob_mappings) configs = normalize_base_packages_to_configs # Validate all configs configs.each do |config| result = config.validate raise ValidationFailedError, result if result.invalid? end if @verbose puts "Detecting conflicts in #{configs.size} package(s)..." end # Detect conflicts detector = PackageConflictDetector.new(configs) report = detector.detect_conflicts if @verbose && report.has_conflicts? puts "⚠️ #{report.total_conflicts} conflict(s) detected" elsif @verbose puts "✓ No conflicts detected" end # Resolve conflicts (may raise PackageMergeError) resolver = PackageConflictResolver.new(report, report.package_sources) ordered_sources = resolver.resolve if @verbose puts "Loading #{ordered_sources.size} package(s) in priority order..." end # Load packages in resolved order ordered_sources.each_with_index do |source, idx| if @verbose print "\r[#{idx + 1}/#{ordered_sources.size}] #{File.basename(source.package_path)}" $stdout.flush end load_package_with_filtering(source, glob_mappings) end puts "\n✓ All packages merged successfully" if @verbose end |
#load_package_with_filtering(package_source, _glob_mappings) ⇒ Object
Load a single package with schema filtering
881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 881 def load_package_with_filtering(package_source, _glob_mappings) repo = package_source.repository # Get all schemas from the package all_schemas = repo.instance_variable_get(:@parsed_schemas) || {} # Apply schema filtering from config filtered_schemas = all_schemas.select do |path, _schema| package_source.include_schema?(path) end # Apply namespace remapping if configured if package_source.namespace_remapping.any? filtered_schemas = apply_namespace_remapping_to_schemas( filtered_schemas, package_source.namespace_remapping, ) end # Merge filtered schemas into current repository @parsed_schemas.merge!(filtered_schemas) # Merge files list @files ||= [] filtered_files = (repo.files || []).select do |file| package_source.include_schema?(file) end @files.concat(filtered_files) # Merge namespace mappings repo.namespace_mappings&.each do |mapping| configure_namespace(prefix: mapping.prefix, uri: mapping.uri) end # Merge schema location mappings repo.schema_location_mappings&.each do |mapping| @schema_location_mappings ||= [] unless @schema_location_mappings.any? { |m| m.from == mapping.from } @schema_location_mappings << mapping end end end |
#namespace_prefix_details ⇒ Array<NamespacePrefixInfo>
Get detailed namespace prefix information
523 524 525 526 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 523 def namespace_prefix_details manager = NamespacePrefixManager.new(self) manager.detailed_prefix_info end |
#namespace_summary ⇒ Array<Hash>
Namespace summary
502 503 504 505 506 507 508 509 510 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 502 def namespace_summary all_namespaces.map do |ns| { uri: ns, prefix: namespace_to_prefix(ns), types: types_in_namespace(ns).size, } end end |
#namespace_to_prefix(namespace_uri) ⇒ String?
Get the namespace prefix for a URI
515 516 517 518 519 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 515 def namespace_to_prefix(namespace_uri) return nil if namespace_uri.nil? || namespace_uri.empty? @namespace_registry.get_primary_prefix(namespace_uri) end |
#needs_parsing? ⇒ Boolean
Check if repository needs parsing Used by demo scripts to determine if parse() should be called
635 636 637 638 639 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 635 def needs_parsing? # Check if schemas are already in the global cache # (either from package loading or previous parse) get_all_processed_schemas.empty? end |
#normalize_base_packages_to_configs ⇒ Array<BasePackageConfig>
Normalize base_packages to BasePackageConfig objects Handles both legacy string format and new hash/config format
811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 811 def normalize_base_packages_to_configs (base_packages || []).map do |pkg| case pkg when String BasePackageConfig.new(package: pkg) when Hash # Convert string keys to symbols for BasePackageConfig symbolized = pkg.transform_keys do |k| k.is_a?(String) ? k.to_sym : k end BasePackageConfig.new(**symbolized) when BasePackageConfig pkg else # Fallback for any other type BasePackageConfig.new(package: pkg.to_s) end end end |
#parse(schema_locations: {}, lazy_load: true, verbose: false) ⇒ self
Parse XSD schemas from configured files and base packages
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 68 def parse(schema_locations: {}, lazy_load: true, verbose: false) @lazy_load = lazy_load @verbose = verbose # Register namespace mappings loaded from YAML with the namespace registry if namespace_mappings && !namespace_mappings.empty? namespace_mappings.each do |mapping| @namespace_registry.register(mapping.prefix, mapping.uri) end end # Convert schema_location_mappings to Glob format glob_mappings = (schema_location_mappings || []).map(&:to_glob_format) # Add any additional schema locations if schema_locations && !schema_locations.empty? schema_locations.each do |from, to| glob_mappings << { from: from, to: to } end end # Load base packages first - auto-detect which method to use if base_packages&.any? if supports_conflict_detection? load_base_packages_with_conflict_detection(glob_mappings) else load_base_packages(glob_mappings) end end # Parse each schema file with progress indicators if @verbose puts "Parsing #{(files || []).size} schema files..." (files || []).each_with_index do |file_path, idx| print "\r[#{idx + 1}/#{(files || []).size}] #{File.basename(file_path)}" $stdout.flush parse_schema_file(file_path, glob_mappings) end puts "\n✓ All schemas parsed" else (files || []).each do |file_path| parse_schema_file(file_path, glob_mappings) end end self end |
#parse_qualified_name(qualified_name) ⇒ Hash?
Parse a qualified name into its components
412 413 414 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 412 def parse_qualified_name(qualified_name) QualifiedNameParser.parse(qualified_name, @namespace_registry) end |
#remap_namespace_prefixes(changes) ⇒ SchemaRepository
Remap namespace prefixes
531 532 533 534 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 531 def remap_namespace_prefixes(changes) remapper = NamespaceRemapper.new(self) remapper.remap(changes) end |
#resolve(verbose: false) ⇒ self
Force full resolution of all imports/includes and build indexes
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 119 def resolve(verbose: false) return self if @resolved @verbose = verbose # Get all processed schemas including imports/includes all_schemas = get_all_processed_schemas if @verbose total_imports = count_total_imports(all_schemas) if total_imports.positive? puts "Resolving #{total_imports} schema dependencies..." processed = 0 all_schemas.each_value do |schema| imports = schema.respond_to?(:import) ? schema.import : [] (imports || []).each do |import| processed += 1 namespace_info = import.respond_to?(:namespace) ? (import.namespace || "no namespace") : "unknown" print "\r[#{processed}/#{total_imports}] #{namespace_info}" $stdout.flush end end puts "\n✓ All dependencies resolved" else puts "✓ No schema dependencies to resolve" end end # Extract namespaces from parsed schemas if not configured if namespace_mappings.nil? || namespace_mappings.empty? @namespace_registry.extract_from_schemas(all_schemas.values) else # Register namespace mappings from configuration namespace_mappings.each do |mapping| @namespace_registry.register(mapping.prefix, mapping.uri) end end # Build type index from all parsed schemas (including imported/included) @type_index.build_from_schemas(all_schemas) @resolved = true self end |
#schemas ⇒ Hash
Get all schemas (alias for compatibility)
572 573 574 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 572 def schemas get_all_processed_schemas end |
#statistics ⇒ Hash
Get repository statistics
418 419 420 421 422 423 424 425 426 427 428 429 430 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 418 def statistics type_stats = @type_index.statistics { total_schemas: @parsed_schemas.size, total_types: type_stats[:total_types], types_by_category: type_stats[:by_type], total_namespaces: type_stats[:namespaces], namespace_prefixes: @namespace_registry.all_prefixes.size, resolved: @resolved, validated: @validated, } end |
#supports_conflict_detection? ⇒ Boolean
Check if base_packages contains configuration objects
926 927 928 929 930 931 932 933 934 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 926 def supports_conflict_detection? return false unless base_packages&.any? # If any element is a Hash or BasePackageConfig, use conflict detection base_packages.any? do |pkg| pkg.is_a?(Hash) || pkg.is_a?(BasePackageConfig) || (pkg.is_a?(String) && pkg.start_with?("{")) end end |
#to_package(output_path, xsd_mode: :include_all, resolution_mode: :resolved, serialization_format: :marshal, metadata: {}) ⇒ SchemaRepositoryPackage
Export repository as a ZIP package with schemas and metadata
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 611 def to_package(output_path, xsd_mode: :include_all, resolution_mode: :resolved, serialization_format: :marshal, metadata: {}) # Ensure repository is resolved if creating resolved package resolve unless @resolved || resolution_mode == :bare # Create package configuration config = PackageConfiguration.new( xsd_mode: xsd_mode, resolution_mode: resolution_mode, serialization_format: serialization_format, ) # Delegate to SchemaRepositoryPackage SchemaRepositoryPackage.create( repository: self, output_path: output_path, config: config, metadata: , ) end |
#type_exists?(qualified_name) ⇒ Boolean
Quick type existence check
449 450 451 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 449 def type_exists?(qualified_name) find_type(qualified_name).resolved? end |
#validate(strict: false) ⇒ Array<String>
Validate the repository
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 168 def validate(strict: false) errors = [] # Check that all files exist and are accessible (files || []).each do |file_path| next if File.exist?(file_path) error = "Schema file not found: #{file_path}" errors << error raise Error, error if strict end # Check that all schemas were parsed successfully missing_schemas = (files || []).reject { |f| @parsed_schemas.key?(f) } unless missing_schemas.empty? error = "Failed to parse schemas: #{missing_schemas.join(', ')}" errors << error raise Error, error if strict end # Check for circular imports (simple check) check_circular_imports(errors, strict) # Check that namespace mappings are valid (namespace_mappings || []).each do |mapping| if mapping.prefix.nil? || mapping.prefix.empty? error = "Invalid namespace mapping: prefix cannot be empty" errors << error raise Error, error if strict end next unless mapping.uri.nil? || mapping.uri.empty? error = "Invalid namespace mapping for prefix '#{mapping.prefix}': URI cannot be empty" errors << error raise Error, error if strict end @validated = errors.empty? errors end |
#validate_xsd_spec(version: "1.0") ⇒ SpecComplianceReport
Validate XSD specification compliance
558 559 560 561 562 |
# File 'lib/lutaml/xsd/schema_repository.rb', line 558 def validate_xsd_spec(version: "1.0") require_relative "xsd_spec_validator" validator = XsdSpecValidator.new(self, version: version) validator.validate end |