lutaml-model 0.8.4 → 0.8.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependent-tests.yml +5 -0
  3. data/.rubocop.yml +18 -0
  4. data/.rubocop_todo.yml +91 -22
  5. data/Gemfile +2 -0
  6. data/README.adoc +114 -2
  7. data/docs/_guides/index.adoc +18 -0
  8. data/docs/_guides/jsonld-serialization.adoc +217 -0
  9. data/docs/_guides/rdf-serialization.adoc +344 -0
  10. data/docs/_guides/turtle-serialization.adoc +224 -0
  11. data/docs/_migrations/0-8-0-namespace-restructuring.adoc +90 -0
  12. data/docs/_pages/serialization_adapters.adoc +31 -0
  13. data/docs/_references/index.adoc +1 -0
  14. data/docs/_references/rdf-namespaces.adoc +243 -0
  15. data/docs/index.adoc +3 -2
  16. data/lib/lutaml/jsonld/adapter.rb +23 -0
  17. data/lib/lutaml/jsonld/context.rb +69 -0
  18. data/lib/lutaml/jsonld/term_definition.rb +39 -0
  19. data/lib/lutaml/jsonld/transform.rb +174 -0
  20. data/lib/lutaml/jsonld.rb +23 -0
  21. data/lib/lutaml/model/format_registry.rb +10 -1
  22. data/lib/lutaml/model/serialize/format_conversion.rb +17 -1
  23. data/lib/lutaml/model/version.rb +1 -1
  24. data/lib/lutaml/model.rb +6 -0
  25. data/lib/lutaml/rdf/error.rb +7 -0
  26. data/lib/lutaml/rdf/iri.rb +44 -0
  27. data/lib/lutaml/rdf/language_tagged.rb +11 -0
  28. data/lib/lutaml/rdf/literal.rb +62 -0
  29. data/lib/lutaml/rdf/mapping.rb +71 -0
  30. data/lib/lutaml/rdf/mapping_rule.rb +35 -0
  31. data/lib/lutaml/rdf/member_rule.rb +13 -0
  32. data/lib/lutaml/rdf/namespace.rb +58 -0
  33. data/lib/lutaml/rdf/namespace_set.rb +69 -0
  34. data/lib/lutaml/rdf/namespaces/dcterms_namespace.rb +12 -0
  35. data/lib/lutaml/rdf/namespaces/owl_namespace.rb +12 -0
  36. data/lib/lutaml/rdf/namespaces/rdf_namespace.rb +14 -0
  37. data/lib/lutaml/rdf/namespaces/rdfs_namespace.rb +12 -0
  38. data/lib/lutaml/rdf/namespaces/skos_namespace.rb +12 -0
  39. data/lib/lutaml/rdf/namespaces/xsd_namespace.rb +12 -0
  40. data/lib/lutaml/rdf/namespaces.rb +14 -0
  41. data/lib/lutaml/rdf/transform.rb +36 -0
  42. data/lib/lutaml/rdf.rb +19 -0
  43. data/lib/lutaml/turtle/adapter.rb +35 -0
  44. data/lib/lutaml/turtle/mapping.rb +7 -0
  45. data/lib/lutaml/turtle/transform.rb +158 -0
  46. data/lib/lutaml/turtle.rb +22 -0
  47. data/lib/lutaml/xml/adapter/adapter_helpers.rb +1 -42
  48. data/lib/lutaml/xml/adapter/base_adapter.rb +48 -458
  49. data/lib/lutaml/xml/adapter/namespace_data.rb +0 -17
  50. data/lib/lutaml/xml/adapter/namespace_uri_collector.rb +71 -0
  51. data/lib/lutaml/xml/adapter/nokogiri_adapter.rb +5 -1110
  52. data/lib/lutaml/xml/adapter/oga_adapter.rb +6 -846
  53. data/lib/lutaml/xml/adapter/ox_adapter.rb +7 -884
  54. data/lib/lutaml/xml/adapter/plan_based_builder.rb +929 -0
  55. data/lib/lutaml/xml/adapter/rexml_adapter.rb +10 -864
  56. data/lib/lutaml/xml/adapter/xml_parser.rb +86 -0
  57. data/lib/lutaml/xml/adapter/xml_serializer.rb +291 -0
  58. data/lib/lutaml/xml/adapter.rb +0 -1
  59. data/lib/lutaml/xml/adapter_element.rb +7 -1
  60. data/lib/lutaml/xml/builder/base.rb +0 -1
  61. data/lib/lutaml/xml/data_model.rb +9 -1
  62. data/lib/lutaml/xml/document.rb +3 -1
  63. data/lib/lutaml/xml/element.rb +13 -10
  64. data/lib/lutaml/xml/serialization/format_conversion.rb +19 -42
  65. data/lib/lutaml/xml/serialization/instance_methods.rb +26 -35
  66. data/lib/lutaml/xml/transformation/custom_method_wrapper.rb +34 -55
  67. data/lib/lutaml/xml/transformation/rule_applier.rb +1 -1
  68. data/lib/lutaml/xml/xml_element.rb +24 -20
  69. data/spec/lutaml/integration/edge_cases_spec.rb +109 -0
  70. data/spec/lutaml/integration/multi_format_spec.rb +106 -0
  71. data/spec/lutaml/integration/round_trip_spec.rb +170 -0
  72. data/spec/lutaml/jsonld/adapter_spec.rb +46 -0
  73. data/spec/lutaml/jsonld/context_spec.rb +114 -0
  74. data/spec/lutaml/jsonld/term_definition_spec.rb +55 -0
  75. data/spec/lutaml/jsonld/transform_spec.rb +211 -0
  76. data/spec/lutaml/rdf/graph_serialization_spec.rb +137 -0
  77. data/spec/lutaml/rdf/iri_spec.rb +73 -0
  78. data/spec/lutaml/rdf/literal_spec.rb +98 -0
  79. data/spec/lutaml/rdf/mapping_spec.rb +164 -0
  80. data/spec/lutaml/rdf/member_rule_spec.rb +17 -0
  81. data/spec/lutaml/rdf/namespace_set_spec.rb +115 -0
  82. data/spec/lutaml/rdf/namespace_spec.rb +241 -0
  83. data/spec/lutaml/rdf/rdf_transform_spec.rb +82 -0
  84. data/spec/lutaml/turtle/adapter_spec.rb +47 -0
  85. data/spec/lutaml/turtle/mapping_spec.rb +123 -0
  86. data/spec/lutaml/turtle/transform_spec.rb +273 -0
  87. data/spec/lutaml/xml/adapter/base_adapter_regression_spec.rb +151 -0
  88. data/spec/lutaml/xml/adapter/order_spec.rb +150 -0
  89. data/spec/lutaml/xml/clear_parse_state_spec.rb +139 -0
  90. data/spec/lutaml/xml/doubly_defined_namespace_spec.rb +0 -2
  91. data/spec/lutaml/xml/schema/compiler_spec.rb +75 -69
  92. data/spec/lutaml/xml/transformation/custom_method_wrapper_spec.rb +213 -14
  93. metadata +58 -3
  94. data/lib/lutaml/xml/adapter/xml_serialization.rb +0 -145
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Rdf
5
+ class Namespace
6
+ class << self
7
+ def uri(value = nil)
8
+ return @uri if value.nil?
9
+ if @uri
10
+ raise FrozenError,
11
+ "#{name}.uri is already set to #{@uri.inspect}"
12
+ end
13
+
14
+ @uri = value.freeze
15
+ end
16
+
17
+ def prefix(value = nil)
18
+ return @prefix if value.nil?
19
+ if @prefix
20
+ raise FrozenError,
21
+ "#{name}.prefix is already set to #{@prefix.inspect}"
22
+ end
23
+
24
+ @prefix = value.to_s.freeze
25
+ end
26
+
27
+ def [](local_name)
28
+ "#{uri}#{local_name}"
29
+ end
30
+
31
+ def prefixed(local_name)
32
+ "#{prefix}:#{local_name}"
33
+ end
34
+
35
+ def resolve_compact_iri(compact_iri, namespaces)
36
+ return compact_iri unless compact_iri.include?(":")
37
+
38
+ pfx, local = compact_iri.split(":", 2)
39
+ ns = namespaces.find { |n| n.prefix == pfx }
40
+ ns ? ns[local] : compact_iri
41
+ end
42
+
43
+ def ==(other)
44
+ other.is_a?(Class) && other < Namespace &&
45
+ other.uri == uri && other.prefix == prefix
46
+ end
47
+
48
+ def hash
49
+ [uri, prefix].hash
50
+ end
51
+
52
+ def to_s
53
+ "#{name}(prefix: #{prefix.inspect}, uri: #{uri.inspect})"
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Rdf
5
+ class NamespaceSet
6
+ include Enumerable
7
+
8
+ def initialize(*namespace_classes)
9
+ @by_prefix = {}
10
+ namespace_classes.each { |ns| add(ns) }
11
+ end
12
+
13
+ def add(namespace_class)
14
+ pfx = namespace_class.prefix
15
+ if @by_prefix.key?(pfx) && @by_prefix[pfx] != namespace_class
16
+ raise ArgumentError,
17
+ "Prefix '#{pfx}' conflicts: #{@by_prefix[pfx].name} vs #{namespace_class.name}"
18
+ end
19
+ @by_prefix[pfx] = namespace_class
20
+ self
21
+ end
22
+
23
+ def [](prefix)
24
+ @by_prefix[prefix]
25
+ end
26
+
27
+ def resolve_compact_iri(compact_iri)
28
+ Namespace.resolve_compact_iri(compact_iri, to_a)
29
+ end
30
+
31
+ def compact(full_uri)
32
+ each do |ns|
33
+ next unless full_uri.start_with?(ns.uri)
34
+
35
+ local = full_uri.delete_prefix(ns.uri)
36
+ return ns.prefixed(local)
37
+ end
38
+ nil
39
+ end
40
+
41
+ def each(&)
42
+ @by_prefix.each_value(&)
43
+ end
44
+
45
+ def size
46
+ @by_prefix.size
47
+ end
48
+
49
+ def empty?
50
+ @by_prefix.empty?
51
+ end
52
+
53
+ def to_a
54
+ @by_prefix.values
55
+ end
56
+
57
+ def to_hash
58
+ @by_prefix.transform_values(&:uri)
59
+ end
60
+
61
+ def merge(other_set)
62
+ return self if equal?(other_set)
63
+
64
+ other_set.each { |ns| add(ns) }
65
+ self
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Rdf
5
+ module Namespaces
6
+ class DctermsNamespace < Lutaml::Rdf::Namespace
7
+ uri "http://purl.org/dc/terms/"
8
+ prefix "dcterms"
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Rdf
5
+ module Namespaces
6
+ class OwlNamespace < Lutaml::Rdf::Namespace
7
+ uri "http://www.w3.org/2002/07/owl#"
8
+ prefix "owl"
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Rdf
5
+ module Namespaces
6
+ class RdfSyntaxNamespace < Lutaml::Rdf::Namespace
7
+ uri "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8
+ prefix "rdf"
9
+ end
10
+
11
+ RdfNamespace = RdfSyntaxNamespace
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Rdf
5
+ module Namespaces
6
+ class RdfsNamespace < Lutaml::Rdf::Namespace
7
+ uri "http://www.w3.org/2000/01/rdf-schema#"
8
+ prefix "rdfs"
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Rdf
5
+ module Namespaces
6
+ class SkosNamespace < Lutaml::Rdf::Namespace
7
+ uri "http://www.w3.org/2004/02/skos/core#"
8
+ prefix "skos"
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Rdf
5
+ module Namespaces
6
+ class XsdNamespace < Lutaml::Rdf::Namespace
7
+ uri "http://www.w3.org/2001/XMLSchema#"
8
+ prefix "xsd"
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Rdf
5
+ module Namespaces
6
+ autoload :SkosNamespace, "#{__dir__}/namespaces/skos_namespace"
7
+ autoload :DctermsNamespace, "#{__dir__}/namespaces/dcterms_namespace"
8
+ autoload :RdfNamespace, "#{__dir__}/namespaces/rdf_namespace"
9
+ autoload :RdfsNamespace, "#{__dir__}/namespaces/rdfs_namespace"
10
+ autoload :OwlNamespace, "#{__dir__}/namespaces/owl_namespace"
11
+ autoload :XsdNamespace, "#{__dir__}/namespaces/xsd_namespace"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Rdf
5
+ class Transform < Lutaml::Model::Transform
6
+ protected
7
+
8
+ def resolve_subject_uri(mapping, instance)
9
+ mapping.rdf_subject&.call(instance)
10
+ end
11
+
12
+ def resolve_type_uri(mapping)
13
+ return unless mapping.rdf_type
14
+
15
+ mapping.namespace_set.resolve_compact_iri(mapping.rdf_type)
16
+ end
17
+
18
+ def resolve_type_compact(mapping)
19
+ mapping.rdf_type
20
+ end
21
+
22
+ def build_instance(attrs, options)
23
+ child_register = Lutaml::Model::Register.resolve_for_child(
24
+ model_class, lutaml_register
25
+ )
26
+ instance = model_class.new(attrs.merge(lutaml_register: child_register))
27
+ root_and_parent_assignment(instance, options)
28
+ instance
29
+ end
30
+
31
+ def extract_language(value)
32
+ value.language_tag if value.is_a?(Lutaml::Rdf::LanguageTagged)
33
+ end
34
+ end
35
+ end
36
+ end
data/lib/lutaml/rdf.rb ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "model"
4
+
5
+ module Lutaml
6
+ module Rdf
7
+ autoload :Error, "#{__dir__}/rdf/error"
8
+ autoload :Iri, "#{__dir__}/rdf/iri"
9
+ autoload :LanguageTagged, "#{__dir__}/rdf/language_tagged"
10
+ autoload :Literal, "#{__dir__}/rdf/literal"
11
+ autoload :Namespace, "#{__dir__}/rdf/namespace"
12
+ autoload :NamespaceSet, "#{__dir__}/rdf/namespace_set"
13
+ autoload :Mapping, "#{__dir__}/rdf/mapping"
14
+ autoload :MappingRule, "#{__dir__}/rdf/mapping_rule"
15
+ autoload :MemberRule, "#{__dir__}/rdf/member_rule"
16
+ autoload :Namespaces, "#{__dir__}/rdf/namespaces"
17
+ autoload :Transform, "#{__dir__}/rdf/transform"
18
+ end
19
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Turtle
5
+ class Adapter
6
+ attr_reader :data, :register
7
+
8
+ def initialize(data, register: nil)
9
+ @data = data
10
+ @register = register || Lutaml::Model::Config.default_register
11
+ end
12
+
13
+ def self.parse(turtle_string, _options = {})
14
+ require "rdf/turtle"
15
+ graph = RDF::Graph.new
16
+ RDF::Turtle::Reader.new(turtle_string).each_statement do |stmt|
17
+ graph << stmt
18
+ end
19
+ graph
20
+ end
21
+
22
+ def to_turtle(_options = {})
23
+ require "rdf/turtle"
24
+ case data
25
+ when String
26
+ data
27
+ when RDF::Enumerable
28
+ RDF::Turtle::Writer.buffer { |w| data.each_statement { |s| w << s } }
29
+ else
30
+ data.to_s
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Turtle
5
+ class Mapping < Lutaml::Rdf::Mapping; end
6
+ end
7
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Turtle
5
+ class MissingSubjectError < Lutaml::Rdf::Error; end
6
+
7
+ class Transform < Lutaml::Rdf::Transform
8
+ def model_to_data(instance, _format, options = {})
9
+ require "rdf/turtle"
10
+ mapping = extract_turtle_mapping(options)
11
+ return "" unless mapping
12
+
13
+ if !mapping.rdf_subject && mapping.rdf_predicates.any? && mapping.rdf_members.empty?
14
+ raise MissingSubjectError,
15
+ "Turtle mapping requires a subject block"
16
+ end
17
+
18
+ graph = build_graph(mapping, instance)
19
+ return "" if graph.empty?
20
+
21
+ prefixes = build_prefixes(mapping, instance)
22
+ RDF::Turtle::Writer.buffer(prefixes: prefixes) do |writer|
23
+ graph.each_statement { |stmt| writer << stmt }
24
+ end.strip
25
+ end
26
+
27
+ def data_to_model(data, _format, options = {})
28
+ require "rdf/turtle"
29
+ mapping = extract_turtle_mapping(options)
30
+ unless mapping&.rdf_subject
31
+ raise MissingSubjectError,
32
+ "Turtle mapping requires a subject block"
33
+ end
34
+
35
+ graph = data.is_a?(RDF::Graph) ? data : Lutaml::Turtle::Adapter.parse(data)
36
+ attrs = extract_attributes(graph, mapping)
37
+ build_instance(attrs, options)
38
+ end
39
+
40
+ private
41
+
42
+ def extract_turtle_mapping(options)
43
+ options[:mappings] || mappings_for(:turtle, lutaml_register)
44
+ end
45
+
46
+ def build_graph(mapping, instance)
47
+ graph = RDF::Graph.new
48
+
49
+ has_predicates_or_type = mapping.rdf_type || mapping.rdf_predicates.any?
50
+
51
+ if has_predicates_or_type
52
+ subject_uri = if mapping.rdf_subject
53
+ RDF::URI(resolve_subject_uri(mapping, instance))
54
+ else
55
+ RDF::Node.new
56
+ end
57
+
58
+ if mapping.rdf_type
59
+ type_uri = RDF::URI(resolve_type_uri(mapping))
60
+ graph << RDF::Statement.new(subject_uri, RDF.type, type_uri)
61
+ end
62
+
63
+ mapping.rdf_predicates.each do |rule|
64
+ value = instance.public_send(rule.to)
65
+ next if value.nil?
66
+
67
+ Array(value).each do |v|
68
+ object = build_rdf_object(v, rule)
69
+ graph << RDF::Statement.new(subject_uri, RDF::URI(rule.uri),
70
+ object)
71
+ end
72
+ end
73
+ end
74
+
75
+ mapping.rdf_members.each do |member_rule|
76
+ collection = Array(instance.public_send(member_rule.attr_name))
77
+ collection.each do |member|
78
+ member_mapping = member.class.mappings[:turtle]
79
+ next unless member_mapping
80
+
81
+ graph << build_graph(member_mapping, member)
82
+ end
83
+ end
84
+
85
+ graph
86
+ end
87
+
88
+ def build_rdf_object(value, rule)
89
+ if rule.lang_tagged
90
+ lang = extract_language(value)
91
+ RDF::Literal.new(value.to_s, language: lang)
92
+ else
93
+ case value
94
+ when Integer then RDF::Literal.new(value, datatype: RDF::XSD.integer)
95
+ when Float then RDF::Literal.new(value, datatype: RDF::XSD.double)
96
+ when TrueClass, FalseClass then RDF::Literal.new(value, datatype: RDF::XSD.boolean)
97
+ else RDF::Literal.new(value.to_s)
98
+ end
99
+ end
100
+ end
101
+
102
+ def build_prefixes(mapping, instance)
103
+ ns_set = mapping.namespace_set
104
+
105
+ mapping.rdf_members.each do |member_rule|
106
+ collection = Array(instance.public_send(member_rule.attr_name))
107
+ next if collection.empty?
108
+
109
+ member_mapping = collection.first.class.mappings[:turtle]
110
+ next unless member_mapping
111
+
112
+ ns_set = ns_set.merge(member_mapping.namespace_set)
113
+ end
114
+
115
+ ns_set.each.with_object({}) do |ns, h|
116
+ h[ns.prefix.to_sym] = ns.uri if ns.prefix && ns.uri
117
+ end
118
+ end
119
+
120
+ def extract_attributes(graph, mapping)
121
+ attrs = {}
122
+ type_uri = resolve_type_uri(mapping)
123
+
124
+ matching_subjects = find_subjects_by_type(graph, type_uri)
125
+
126
+ matching_subjects.each do |subject|
127
+ mapping.rdf_predicates.each do |rule|
128
+ stmts = graph.query([subject, RDF::URI(rule.uri), nil])
129
+ next if stmts.empty?
130
+
131
+ values = stmts.map { |s| literal_to_ruby(s.object) }
132
+ attrs[rule.to] = values.length == 1 ? values.first : values
133
+ end
134
+ end
135
+
136
+ attrs
137
+ end
138
+
139
+ def find_subjects_by_type(graph, type_uri)
140
+ graph.query([nil, RDF.type, RDF::URI(type_uri)]).map(&:subject).uniq
141
+ end
142
+
143
+ def literal_to_ruby(rdf_object)
144
+ case rdf_object
145
+ when RDF::Literal
146
+ case rdf_object.datatype
147
+ when RDF::XSD.integer then rdf_object.value.to_i
148
+ when RDF::XSD.double, RDF::XSD.decimal, RDF::XSD.float then rdf_object.value.to_f
149
+ when RDF::XSD.boolean then rdf_object.value == "true"
150
+ else rdf_object.value
151
+ end
152
+ else
153
+ rdf_object.to_s
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "model"
4
+ require_relative "rdf"
5
+
6
+ module Lutaml
7
+ module Turtle
8
+ autoload :Mapping, "#{__dir__}/turtle/mapping"
9
+ autoload :Transform, "#{__dir__}/turtle/transform"
10
+ autoload :Adapter, "#{__dir__}/turtle/adapter"
11
+ end
12
+ end
13
+
14
+ Lutaml::Model::FormatRegistry.register(
15
+ :turtle,
16
+ mapping_class: Lutaml::Turtle::Mapping,
17
+ adapter_class: Lutaml::Turtle::Adapter,
18
+ transformer: Lutaml::Turtle::Transform,
19
+ key_value: false,
20
+ rdf: true,
21
+ error_types: ["RDF::ReaderError"],
22
+ )
@@ -29,7 +29,7 @@ module Lutaml
29
29
  when Moxml::Cdata
30
30
  "#cdata-section"
31
31
  when Moxml::ProcessingInstruction
32
- "processing_instruction"
32
+ element.target
33
33
  when Moxml::Comment
34
34
  "comment"
35
35
  else
@@ -102,47 +102,6 @@ module Lutaml
102
102
  def text_node?(node)
103
103
  TEXT_CLASSES.include?(node.class)
104
104
  end
105
-
106
- # Build Element object from child node
107
- #
108
- # @param child [Moxml::Node] The child node
109
- # @return [Element] The Element object
110
- def build_element_from_child(child)
111
- if text_node?(child)
112
- Element.new("Text", "text", node_type: :text)
113
- else
114
- Element.new("Element", name_of(child), node_type: :element)
115
- end
116
- end
117
-
118
- # Get the prefix for a namespace URI from hoisted declarations
119
- #
120
- # This checks if a namespace URI is already declared in hoisted_declarations
121
- # and returns the prefix that should be used. This prevents duplicate xmlns
122
- # declarations when both hoisted_declarations and attribute code try to add
123
- # the same namespace.
124
- #
125
- # @param namespace_uri [String] The namespace URI to look up
126
- # @param hoisted_declarations [Hash{String, nil => String}] The hoisted declarations
127
- # (keys are prefixes or nil for default, values are URIs)
128
- # @return [String, nil] The prefix if found, nil if not hoisted
129
- def prefix_for_namespace_uri(namespace_uri, hoisted_declarations)
130
- return nil unless hoisted_declarations
131
-
132
- hoisted_declarations.each do |prefix, uri|
133
- return prefix if uri == namespace_uri
134
- end
135
- nil
136
- end
137
-
138
- # Check if a namespace URI is already hoisted
139
- #
140
- # @param namespace_uri [String] The namespace URI to check
141
- # @param hoisted_declarations [Hash{String, nil => String}] The hoisted declarations
142
- # @return [Boolean] true if the namespace is already hoisted
143
- def namespace_uri_hoisted?(namespace_uri, hoisted_declarations)
144
- prefix_for_namespace_uri(namespace_uri, hoisted_declarations) != nil
145
- end
146
105
  end
147
106
  end
148
107
  end