Skip to content

Commit

Permalink
Updated conversion methods and specs
Browse files Browse the repository at this point in the history
  • Loading branch information
suleman-uzair committed Nov 25, 2024
1 parent 35d0ccd commit ee53087
Show file tree
Hide file tree
Showing 10 changed files with 148 additions and 52 deletions.
24 changes: 16 additions & 8 deletions lib/lutaml/xsd/glob.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
module Lutaml
module Xsd
module Glob
module_function
extend self

def set_path_or_url(location)
return nullify_path_and_url if location.nil?
return nullify_location if location.nil?

@location = location
@url = location if location.start_with?(/http\w?:\/{2}[^.]+/)
Expand All @@ -31,16 +31,24 @@ 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
def http_get(url)
Net::HTTP.get(URI.parse(url))
end

def include_schema(schema_location)
return unless location? || schema_location

schema_path = schema_location_path(schema_location)
url? ? http_get(schema_path) : File.read(schema_path)
end

private

def schema_location_path(schema_location)
separator = "/" unless schema_location&.start_with?("/") || location&.end_with?("/")
[location, schema_location].join(separator)
end

def nullify_location
@location = nil
@path = nil
Expand Down
15 changes: 2 additions & 13 deletions lib/lutaml/xsd/import.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module Xsd
class Import < Lutaml::Model::Serializable
attribute :id, :string
attribute :namespace, :string
attribute :annotation, Annotation
attribute :schema_location, :string

xml do
Expand All @@ -17,19 +18,7 @@ class Import < Lutaml::Model::Serializable
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
Glob.include_schema(schema_location) if schema_location
end
end
end
Expand Down
9 changes: 8 additions & 1 deletion lib/lutaml/xsd/include.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,21 @@
module Lutaml
module Xsd
class Include < Lutaml::Model::Serializable
attribute :annotation, Annotation, collection: true
attribute :id, :string
attribute :schema_location, :string
attribute :annotation, Annotation

xml do
root "include", mixed: true
namespace "http://www.w3.org/2001/XMLSchema", "xsd"

map_attribute :id, to: :id
map_element :annotation, to: :annotation
end

def include_schema
Glob.include_schema(schema_location)
end
end
end
end
111 changes: 87 additions & 24 deletions lib/lutaml/xsd/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ module Lutaml
module Xsd
class Schema < Lutaml::Model::Serializable
attribute :xmlns, :string
attribute :imports, Import, collection: true
attribute :includes, Include, collection: true
attribute :import, Import, 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 :include, Include, collection: true
attribute :complex_type, ComplexType, collection: true
attribute :simple_type, SimpleType, collection: true
attribute :group, Group, collection: true
Expand All @@ -23,8 +24,8 @@ 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_xml }
map_element :include, to: :include
map_element :import, to: :import, with: { from: :import_from_schema, to: :import_to_schema }
map_element :include, to: :include, with: { from: :include_from_schema, to: :include_to_schema }
map_element :element, to: :element
map_element :complexType, to: :complex_type
map_element :simpleType, to: :simple_type
Expand All @@ -40,41 +41,103 @@ def self.processed_schemas
@processed_schemas ||= {}
end

def self.schema_processed?(id)
processed_schemas[id]
def self.schema_processed?(location)
processed_schemas[location]
end

def self.schema_processed(id)
processed_schemas[id] = true
def self.schema_processed(location)
processed_schemas[location] = true
end

def from_schema(model, value)
model.import_and_include += value
model.import_and_include&.flatten!&.uniq!
def import_from_schema(model, value)
model.imports += value
model.imports&.flatten!&.uniq!

value.each do |imported_schema|
next if self.class.schema_processed?(imported_schema["id"])
next if self.class.schema_processed?(dig_schema_location(imported_schema))

self.class.schema_processed(imported_schema["id"])
imported_schema["schema_location"] = imported_schema.delete("__schema_location")&.dig(:schema_location)
self.class.schema_processed(dig_schema_location(imported_schema))
schema_imported = Import.new(
id: imported_schema["id"],
namespace: imported_schema["namespace"],
schema_location: imported_schema["schema_location"],
schema_location: dig_schema_location(imported_schema),
).import_schema
model.schemas << Lutaml::Xsd.parse(schema_imported, location: Glob.location) if schema_imported
end
end

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)
def import_to_schema(model, parent, doc)
model.imports.each_with_index do |imported_schema, index|
import_element = create_import_or_include_element(imported_schema, doc, element_name: "import")
insert_annotation(imported_schema, import_element, doc)
parent.add_child(import_element)
model.imports.delete_at(index)
end
end

def include_from_schema(model, value)
model.includes += value
model.includes&.flatten!&.uniq!

value.each do |included_schema|
next if self.class.schema_processed?(dig_schema_location(included_schema))

self.class.schema_processed(dig_schema_location(included_schema))
schema_included = Include.new(
id: included_schema["id"],
schema_location: dig_schema_location(included_schema),
).include_schema
model.schemas << Lutaml::Xsd.parse(schema_included, location: Glob.location) if schema_included
end
end

def include_to_schema(model, parent, doc)
model.includes.each_with_index do |included_schema, index|
include_element = create_import_or_include_element(included_schema, doc, element_name: "include")
insert_annotation(included_schema, include_element, doc)
parent.add_child(include_element)
model.includes.delete_at(index)
end
end

def dig_schema_location(schema_hash)
schema_hash&.dig("__schema_location", :schema_location)
end

def schema_location?(schema_hash)
schema_hash&.dig("__schema_location")&.key?(:schema_location)
end

def reject_text(schema_hash)
schema_hash&.reject! { |k, _| k if k == "text" }
end

def add_annotation(value, doc)
annotation = doc.create_element("annotation")
documentation_element = doc.create_element("documentation")
documentation_element.add_child(documentation_text(value))
annotation.add_child(documentation_element)
annotation
end

def documentation_text(value)
documentation_key = value.keys.find { |key| key.include?("documentation") }
value.dig(documentation_key, "text")
end

def create_import_or_include_element(schema_hash, doc, element_name:)
element = doc.create_element(element_name)
element.set_attribute("id", schema_hash["id"]) if schema_hash["id"]
element.set_attribute("namespace", schema_hash["namespace"]) if schema_hash["namespace"]
element.set_attribute("schemaLocation", dig_schema_location(schema_hash)) if schema_location?(schema_hash)
element
end

def insert_annotation(schema_hash, element, doc)
schema_hash.each do |key, value|
next unless key.include?("annotation")

element.add_child(add_annotation(value, doc))
end
end
end
Expand Down
9 changes: 9 additions & 0 deletions spec/lutaml/fixtures/import-without-location.xsd
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="qualified" blockDefault="#all" targetNamespace="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<xsd:import id="w" namespace="http://schemas.openxmlformats.org/wordprocessingml/2006/main" />
<xsd:import id="xml" namespace="http://www.w3.org/XML/1998/namespace">
<xsd:annotation>
<xsd:documentation>XML Namespace</xsd:documentation>
</xsd:annotation>
</xsd:import>
</xsd:schema>
2 changes: 1 addition & 1 deletion spec/lutaml/fixtures/metaschema-meta-constraints.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
xmlns="http://csrc.nist.gov/ns/oscal/metaschema/1.0"
targetNamespace="http://csrc.nist.gov/ns/oscal/metaschema/1.0">
<!-- <xs:include schemaLocation="metaschema.xsd"/> -->
<xs:include schemaLocation="metaschema.xsd"/>

<xs:simpleType name="MetaschemaPathStringType">
<xs:restriction base="StringDatatype"/>
Expand Down
4 changes: 2 additions & 2 deletions spec/lutaml/fixtures/metaschema.xsd
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
attributeFormDefault="unqualified" targetNamespace="http://csrc.nist.gov/ns/oscal/metaschema/1.0">
<!--

<xs:include schemaLocation="metaschema-prose-module.xsd">
<xs:annotation>
<xs:documentation>This prose module provides support for line and multiline markup.</xs:documentation>
Expand All @@ -12,7 +12,7 @@
<xs:annotation>
<xs:documentation>This data types module provides support for all other built-in Metaschema data types.</xs:documentation>
</xs:annotation>
</xs:include> -->
</xs:include>

<xs:element name="METASCHEMA">
<xs:annotation>
Expand Down
6 changes: 5 additions & 1 deletion spec/lutaml/fixtures/omml_schema.xsd
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="qualified" blockDefault="#all" targetNamespace="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<xsd:import id="w" namespace="http://schemas.openxmlformats.org/wordprocessingml/2006/main" schemaLocation="/wml.xsd" />
<xsd:import id="xml" namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:import id="xml" namespace="http://www.w3.org/XML/1998/namespace">
<xsd:annotation>
<xsd:documentation>XML Namespace</xsd:documentation>
</xsd:annotation>
</xsd:import>
<xsd:simpleType name="ST_Integer255">
<xsd:annotation>
<xsd:documentation>Integer value (1 to 255)</xsd:documentation>
Expand Down
20 changes: 18 additions & 2 deletions spec/lutaml/xsd_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,16 @@
require "spec_helper"

RSpec.describe Lutaml::Xsd do
# TODO: remove dummy location url
subject(:parsed_schema) { described_class.parse(schema, location: "https://raw.githubusercontent.com/t-yuki/ooxml-xsd/refs/heads/master") }
LOCATIONS = {
omml_schema: "https://raw.githubusercontent.com/t-yuki/ooxml-xsd/refs/heads/master",
"metaschema-meta-constraints": "spec/lutaml/fixtures",
"metaschema-markup-multiline": "spec/lutaml/fixtures",
"metaschema-prose-module": "spec/lutaml/fixtures",
"metaschema-markup-line": "spec/lutaml/fixtures",
metaschema: "spec/lutaml/fixtures",
}

subject(:parsed_schema) { described_class.parse(schema, location: location) }

it "has a version number" do
expect(Lutaml::Xsd::VERSION).not_to be nil
Expand All @@ -13,6 +21,14 @@
Dir.glob(File.expand_path("fixtures/*.xsd", __dir__)).each do |input_file|
context "when parsing #{input_file}" do
let(:schema) { File.read(input_file) }
let(:location) do
file_location = LOCATIONS[File.basename(input_file, ".xsd").to_sym]
if file_location&.start_with?("http")
file_location
elsif file_location
File.expand_path(file_location)
end
end

it "matches a Lutaml::Model::Schema object" do
expect(parsed_schema).to be_a(Lutaml::Xsd::Schema)
Expand Down

0 comments on commit ee53087

Please sign in to comment.