Module: Rsxml::Namespace

Defined in:
lib/rsxml/namespace.rb

Class Method Summary collapse

Class Method Details

.compact_attr_qnames(ns_stack, attrs) ⇒ Object

compact all attribute QNames to Strings



6
7
8
9
10
# File 'lib/rsxml/namespace.rb', line 6

def compact_attr_qnames(ns_stack, attrs)
  Hash[attrs.map do |name,value|
         [compact_qname(ns_stack, name), value]
       end]
end

.compact_qname(ns_stack, name) ⇒ Object

produce a QName String from a [LocalPart, prefix, URI] triple



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/rsxml/namespace.rb', line 26

def compact_qname(ns_stack, name)
  return name if name.is_a?(String)

  local_part, prefix, uri = name
  raise "invalid name: #{name}" if !prefix && uri
  if prefix
    if prefix!="xmlns"
      ns = find_namespace_uri(ns_stack, prefix, uri)
      raise "namespace prefix not bound to a namespace: '#{prefix}'" if ! ns
    end
    [prefix, local_part].map{|s| s.to_s unless s.to_s.empty?}.compact.join(':')
  else
    local_part
  end
end

.explode_attr_qnames(ns_stack, attrs) ⇒ Object

explode attribute QNames to [LocalPart, prefix, URI] triples,



13
14
15
16
17
18
19
20
21
22
23
# File 'lib/rsxml/namespace.rb', line 13

def explode_attr_qnames(ns_stack, attrs)
  Hash[attrs.map do |name, value|
         uq_name = explode_qname(ns_stack, name, true)
         local_part, prefix, uri = uq_name
         if !prefix || prefix==""
           [local_part, value]
         else
           [uq_name, value]
         end
       end]
end

.explode_qname(ns_stack, qname, attr = false) ⇒ Object

split a QName into [LocalPart, prefix, URI] triple. fill out missing uris if necessary, and check QName is well specified



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/rsxml/namespace.rb', line 44

def explode_qname(ns_stack, qname, attr=false)
  if qname.is_a?(Array)
    local_part, prefix, uri = qname
    if uri
      raise "invalid name: #{qname.inspect}" if !prefix
      bound_uri = find_namespace_uri(ns_stack, prefix)
      raise "namespace conflict: prefix '#{prefix}' refers to '#{uri}' and '#{bound_uri}'" if bound_uri && uri != bound_uri
      return [local_part, prefix, uri]
    elsif prefix
      uri = find_namespace_uri(ns_stack, prefix)
      raise "namespace prefix not bound: '#{prefix}'" if !uri
      return [local_part, prefix, uri]
    else
      return local_part
    end
  end

  local_part, prefix = split_qname(qname)
  if prefix
    if prefix=="xmlns" && attr
      [local_part, prefix]
    else
      uri = find_namespace_uri(ns_stack, prefix)
      raise "namespace prefix not bound: '#{prefix}'" if ! uri
      [local_part, prefix, uri]
    end
  else
    if attr
      local_part
    else
      default_uri = find_namespace_uri(ns_stack, "")
      if default_uri
        [local_part, "", default_uri]
      else
        local_part
      end
    end
  end
end

.exploded_namespace_declarations(ns) ⇒ Object

produce a Hash of namespace declaration attributes with exploded QNames, from a Hash of namespace prefix bindings



149
150
151
152
153
154
155
156
157
# File 'lib/rsxml/namespace.rb', line 149

def exploded_namespace_declarations(ns)
  Hash[ns.map do |prefix, uri|
         if prefix==""
           ["xmlns", uri]
         else
           [[prefix, "xmlns"], uri]
         end
       end]
end

.extract_explicit_namespace_bindings(element_name, attrs) ⇒ Object

extract a Hash of prefix=>uri mappings from exploded QName element and attrs



121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/rsxml/namespace.rb', line 121

def extract_explicit_namespace_bindings(element_name, attrs)
  element_name_local_part, element_name_prefix, element_name_uri = element_name
  ns = {}
  ns[element_name_prefix] = element_name_uri if element_name_prefix && element_name_uri

  attrs.each do |name, value|
    attr_local_part, attr_prefix, attr_uri = name
    if attr_prefix && attr_uri
      raise "bindings clash: '#{attr_prefix}'=>'#{ns[attr_prefix]}' , '#{attr_prefix}'=>'#{attr_uri}'" if ns.has_key?(attr_prefix) && ns[attr_prefix]!=attr_uri
      ns[attr_prefix] = attr_uri
    end
  end
  ns
end

.find_namespace_uri(ns_stack, prefix, uri_check = nil) ⇒ Object

returns the namespace uri for a prefix, if declared in the stack



96
97
98
99
100
101
# File 'lib/rsxml/namespace.rb', line 96

def find_namespace_uri(ns_stack, prefix, uri_check=nil)
  tns = ns_stack.reverse.find{|ns| ns.has_key?(prefix)}
  uri = tns[prefix] if tns
  raise "prefix: '#{prefix}' is bound to uri: '#{uri}', but should be '#{uri_check}'" if uri_check && uri && uri!=uri_check
  uri
end

.merge_namespace_bindings(ns1, ns2) ⇒ Object

merges two sets of namespace bindings, raising error on clash



172
173
174
175
176
177
178
179
# File 'lib/rsxml/namespace.rb', line 172

def merge_namespace_bindings(ns1, ns2)
  m = ns1.clone
  ns2.each do |k,v|
    raise "bindings clash: '#{k}'=>'#{m[k]}' , '#{k}'=>'#{v}'" if m.has_key?(k) && m[k]!=v
    m[k]=v
  end
  m
end

.namespace_attributes(ns) ⇒ Object

produces a Hash of compact namespace attributes from a Hash of namespace bindings



161
162
163
164
165
166
167
168
169
# File 'lib/rsxml/namespace.rb', line 161

def namespace_attributes(ns)
  Hash[ns.map do |prefix, uri|
         if prefix==""
           ["xmlns", uri]
         else
           [["xmlns", prefix].join(":"), uri]
         end
       end]
end

.non_ns_attrs_ns_bindings(ns_stack, element_name, attrs) ⇒ Object

given the existing ns_stack of ns bindings, a element_name and it’s attributes, return a pair [non_ns_attrs, ns_bindings] containing non-ns related attributes, and namespace bindings for the current element, both those declared in attributes and declared implicitly through exploded element names



185
186
187
188
189
190
191
192
193
# File 'lib/rsxml/namespace.rb', line 185

def non_ns_attrs_ns_bindings(ns_stack, element_name, attrs)
  non_ns_attrs, ns_declared = partition_namespace_decls(attrs)

  ns_explicit = extract_explicit_namespace_bindings(element_name, attrs)
  ns_undeclared = undeclared_namespace_bindings(ns_stack + [ns_declared], ns_explicit)
  ns_bindings = merge_namespace_bindings(ns_declared, ns_undeclared)

  [non_ns_attrs, ns_bindings]
end

.partition_namespace_decls(attrs) ⇒ Object

split attributes into non-namespace related attrs and prefix=>uri namespace bindings



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/rsxml/namespace.rb', line 104

def partition_namespace_decls(attrs)
  nonns_attrs = []
  ns_bindings = []
  attrs.each do |name, value| 
    local_part, prefix = split_qname(name)
    if prefix && prefix=="xmlns"
      ns_bindings << [local_part, value]
    elsif !prefix && local_part=="xmlns"
      ns_bindings << ["", value]
    else
      nonns_attrs << [name, value]
    end
  end
  [Hash[nonns_attrs], Hash[ns_bindings]]
end

.split_qname(qname) ⇒ Object

split a qname String into a [local_part, prefix] pair



85
86
87
88
89
90
91
92
93
# File 'lib/rsxml/namespace.rb', line 85

def split_qname(qname)
  return qname if qname.is_a?(Array)

  if qname =~ /^[^:]+:[^:]+$/
    [*qname.split(':')].reverse
  else
    qname
  end
end

.undeclared_namespace_bindings(ns_stack, ns_explicit) ⇒ Object

figure out which explicit namespaces need declaring

ns_stack is the stack of namespace bindings ns_explicit is the explicit refs for a element



140
141
142
143
144
# File 'lib/rsxml/namespace.rb', line 140

def undeclared_namespace_bindings(ns_stack, ns_explicit)
  Hash[ns_explicit.map do |prefix,uri|
         [prefix, uri] if !find_namespace_uri(ns_stack, prefix, uri)
       end.compact]
end