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_name, prefix, uri = uq_name
         if !prefix || prefix==""
           [local_name, value]
         else
           [uq_name, value]
         end
       end]
end

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

split a QName into [LocalPart, prefix, URI] triple



43
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
# File 'lib/rsxml/namespace.rb', line 43

def explode_qname(ns_stack, qname, attr=false)
  if qname.is_a?(Array)
    if qname.length>1 && !qname[1].nil?
      return qname
    elsif qname.length>1 && qname[1].nil? && !qname[2].nil?
      raise "invalid name: #{qname.inspect}"
    else
      return qname[0]
    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



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

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

.extract_declared_namespace_bindings(attrs) ⇒ Object

extract a Hash of prefix=>uri mappings declared in attributes



98
99
100
101
102
103
104
105
106
107
# File 'lib/rsxml/namespace.rb', line 98

def extract_declared_namespace_bindings(attrs)
  Hash[attrs.map do |name,value|
         local_part, prefix, uri = split_qname(name)
         if (prefix && prefix == "xmlns")
           [local_part, value]
         elsif (!prefix && local_part == "xmlns")
           ["", value]
         end
       end.compact]
end

.extract_explicit_namespace_bindings(tag, attrs) ⇒ Object

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



110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/rsxml/namespace.rb', line 110

def extract_explicit_namespace_bindings(tag, attrs)
  tag_local_part, tag_prefix, tag_uri = tag
  ns = {}
  ns[tag_prefix] = tag_uri if tag_prefix && tag_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



89
90
91
92
93
94
# File 'lib/rsxml/namespace.rb', line 89

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



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

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_bindings_declarations(ns_stack, tag, attrs) ⇒ Object

given the existing ns_stack of ns bindings, a tag and it’s attributes, return a pair [ns_bindings, ns_additional_decls] containing ns bindings for the stack, and additional required (exploded) namespace declarations to be added to the attributes



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

def namespace_bindings_declarations(ns_stack, tag, attrs)
  ns_declared = extract_declared_namespace_bindings(attrs)
  ns_explicit = extract_explicit_namespace_bindings(tag, attrs)
  ns_undeclared = undeclared_namespace_bindings(ns_stack + [ns_declared], ns_explicit)
  ns_bindings = merge_namespace_bindings(ns_declared, ns_undeclared)

  # and declarations for undeclared namespaces
  ns_additional_decls = exploded_namespace_declarations(ns_undeclared)
  
  [ns_bindings, ns_additional_decls]
end

.split_qname(qname) ⇒ Object

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



78
79
80
81
82
83
84
85
86
# File 'lib/rsxml/namespace.rb', line 78

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 tag



129
130
131
132
133
# File 'lib/rsxml/namespace.rb', line 129

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