diff --git a/lib/lutaml/xsd/attribute.rb b/lib/lutaml/xsd/attribute.rb index 8f5b58d..d58031f 100644 --- a/lib/lutaml/xsd/attribute.rb +++ b/lib/lutaml/xsd/attribute.rb @@ -4,6 +4,7 @@ module Lutaml module Xsd class Attribute < Lutaml::Model::Serializable attribute :use, :string + attribute :ref, :string attribute :name, :string attribute :type, :string attribute :default, :string @@ -15,6 +16,7 @@ class Attribute < Lutaml::Model::Serializable namespace "http://www.w3.org/2001/XMLSchema", "xsd" map_attribute :use, to: :use + map_attribute :ref, to: :ref map_attribute :name, to: :name map_attribute :type, to: :type map_attribute :default, to: :default diff --git a/lib/lutaml/xsd/config.rb b/lib/lutaml/xsd/config.rb deleted file mode 100644 index ec1423f..0000000 --- a/lib/lutaml/xsd/config.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -module Lutaml - module Xsd - module Config - module_function - - def set_path_or_url(location) - return if location.nil? - - @url = location if location.start_with?(/http\w?:\/{2}[^.]+/) - @path = location unless @url - end - - def path - @path - end - - def url - @url - end - - def path? - !@path.nil? - end - - def url? - !@url.nil? - end - end - end -end diff --git a/lib/lutaml/xsd/glob.rb b/lib/lutaml/xsd/glob.rb new file mode 100644 index 0000000..70df192 --- /dev/null +++ b/lib/lutaml/xsd/glob.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module Lutaml + module Xsd + module Glob + module_function + + def set_path_or_url(location) + return nullify_path_and_url if location.nil? + + @location = location + @url = location if location.start_with?(/http\w?:\/{2}[^.]+/) + @path = File.expand_path(location) unless @url + rescue => e + raise Error, "Invalid location: #{location}" + end + + def location + @location + end + + def path? + !@path.nil? + end + + def url? + !@url.nil? + end + + def location? + url? || path? + end + + def schema_location_path(schema_location) + if schema_location.start_with?("/") || location.end_with?("/") + location + schema_location + else + location + "/" + schema_location + end + end + + private + + def nullify_location + @location = nil + @path = nil + @url = nil + end + end + end +end diff --git a/lib/lutaml/xsd/import.rb b/lib/lutaml/xsd/import.rb index 27fec68..8a2cf50 100644 --- a/lib/lutaml/xsd/import.rb +++ b/lib/lutaml/xsd/import.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require "net/http" module Lutaml module Xsd class Import < Lutaml::Model::Serializable @@ -14,6 +15,22 @@ class Import < Lutaml::Model::Serializable map_attribute :id, to: :id map_attribute :namespace, to: :namespace end + + def import_schema + if Glob.location? && schema_location + if Glob.url? + Net::HTTP.get( + URI.parse( + Glob.schema_location_path(schema_location), + ), + ) + else + File.read( + Glob.schema_location_path(schema_location), + ) + end + end + end end end end diff --git a/lib/lutaml/xsd/schema.rb b/lib/lutaml/xsd/schema.rb index 076dad5..f677e9d 100644 --- a/lib/lutaml/xsd/schema.rb +++ b/lib/lutaml/xsd/schema.rb @@ -5,7 +5,8 @@ module Xsd class Schema < Lutaml::Model::Serializable attribute :xmlns, :string attribute :import, Import, collection: true - attribute :schema, Schema, collection: true + attribute :schemas, Schema, collection: true + attribute :import_and_include, :hash, collection: true attribute :include, Include, collection: true attribute :element, Element, collection: true attribute :complex_type, ComplexType, collection: true @@ -22,7 +23,7 @@ class Schema < Lutaml::Model::Serializable namespace "http://www.w3.org/2001/XMLSchema", "xsd" map_attribute :xmlns, to: :xmlns, namespace: "http://csrc.nist.gov/ns/oscal/metaschema/1.0", prefix: nil - map_element :import, to: :import, with: { from: :from_schema, to: :to_schema } + map_element :import, to: :import, with: { from: :from_schema, to: :to_schema_xml } map_element :include, to: :include map_element :element, to: :element map_element :complexType, to: :complex_type @@ -35,16 +36,46 @@ class Schema < Lutaml::Model::Serializable map_attribute :targetNamespace, to: :target_namespace end + def self.processed_schemas + @processed_schemas ||= {} + end + + def self.schema_processed?(id) + processed_schemas[id] + end + + def self.schema_processed(id) + processed_schemas[id] = true + end + def from_schema(model, value) - # IN-PROGRESS - # value.each do |imported_schema| - # binding.irb - # imported_schema.from_schema(model, value) - # end + model.import_and_include += value + model.import_and_include&.flatten!&.uniq! + value.each do |imported_schema| + next if self.class.schema_processed?(imported_schema["id"]) + + self.class.schema_processed(imported_schema["id"]) + imported_schema["schema_location"] = imported_schema.delete("__schema_location")&.dig(:schema_location) + schema_imported = Import.new( + id: imported_schema["id"], + namespace: imported_schema["namespace"], + schema_location: imported_schema["schema_location"], + ).import_schema + model.schemas << Lutaml::Xsd.parse(schema_imported, location: Glob.location) if schema_imported + end end - def to_schema(model, parent, doc) - # binding.irb + def to_schema_xml(model, parent, doc) + model.import_and_include.each_with_index do |imported_schema, index| + import_element = doc.create_element("import") + import_element.set_attribute("id", imported_schema["id"]) if imported_schema["id"] + import_element.set_attribute("namespace", imported_schema["namespace"]) if imported_schema["namespace"] + if imported_schema["__schema_location"]&.key?(:schema_location) + import_element.set_attribute("schemaLocation", imported_schema["__schema_location"][:schema_location]) + end + model.import_and_include.delete_at(index) + parent.add_child(import_element) + end end end end diff --git a/lib/lutaml/xsd/xsd.rb b/lib/lutaml/xsd/xsd.rb index 2079ae1..40f3078 100644 --- a/lib/lutaml/xsd/xsd.rb +++ b/lib/lutaml/xsd/xsd.rb @@ -7,7 +7,7 @@ class Error < StandardError; end module_function def parse(xsd, location: nil) - Config.set_path_or_url(location) + Glob.set_path_or_url(location) Schema.from_xml(xsd) end end diff --git a/spec/lutaml/fixtures/omml_schema.xsd b/spec/lutaml/fixtures/omml_schema.xsd index 994789a..3cf2376 100644 --- a/spec/lutaml/fixtures/omml_schema.xsd +++ b/spec/lutaml/fixtures/omml_schema.xsd @@ -1,6 +1,6 @@ - + diff --git a/spec/lutaml/xsd_spec.rb b/spec/lutaml/xsd_spec.rb index 67c8d13..052aa83 100644 --- a/spec/lutaml/xsd_spec.rb +++ b/spec/lutaml/xsd_spec.rb @@ -4,7 +4,7 @@ RSpec.describe Lutaml::Xsd do # TODO: remove dummy location url - subject(:parsed_schema) { described_class.parse(schema, location: "https://www.something.com/") } + subject(:parsed_schema) { described_class.parse(schema, location: "https://raw.githubusercontent.com/t-yuki/ooxml-xsd/refs/heads/master") } it "has a version number" do expect(Lutaml::Xsd::VERSION).not_to be nil