File: xmppelement.rb

package info (click to toggle)
ruby-xmpp4r 0.5.6-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 1,384 kB
  • sloc: ruby: 17,382; xml: 74; sh: 12; makefile: 4
file content (168 lines) | stat: -rw-r--r-- 4,624 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# =XMPP4R - XMPP Library for Ruby
# License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
# Website::http://xmpp4r.github.io

require 'xmpp4r/rexmladdons'

module Jabber

  ##
  # This class represents an XML element and provides functionality
  # for automatic casting of XML element classes according to their
  # element name and namespace.
  #
  # Deriving classes must met these criteria:
  # * The element name and namespace must be specified by calling
  #   the name_xmlns class method
  # * The class constructor must be callable with no mandatory parameter
  class XMPPElement < REXML::Element
    @@name_xmlns_classes = {}
    @@force_xmlns = false

    ##
    # Specify XML element name and xmlns for a deriving class,
    # this pair and the class will be added to a global pool
    #
    # If the namespace is nil the class is a "wildcard class"
    # matching elements with any xmlns if no other class with
    # that namespace was defined
    def self.name_xmlns(name, xmlns=nil)
      @@name_xmlns_classes[[name, xmlns]] = self
    end

    ##
    # Set whether this element is always built with an xmlns attribute
    def self.force_xmlns(force)
      @@force_xmlns = force
    end

    ##
    # Whether this element is always built with an xmlns attribute
    def self.force_xmlns?
      @@force_xmlns
    end

    ##
    # Find the name and namespace for a given class.
    # This class must have registered these two values
    # by calling name_xmlns at definition time.
    #
    # Raises an exception if none was found
    # klass:: [Class]
    # result:: [String, String] name and namespace
    def self.name_xmlns_for_class(klass)
      klass.ancestors.each do |klass1|
        @@name_xmlns_classes.each do |name_xmlns,k|
          if klass1 == k
            return name_xmlns
          end
        end
      end

      raise NoNameXmlnsRegistered.new(klass)
    end

    ##
    # Find a class for given name and namespace
    # name:: [String]
    # xmlns:: [String]
    # result:: A descendant of XMPPElement or REXML::Element
    def self.class_for_name_xmlns(name, xmlns)
      if @@name_xmlns_classes.has_key? [name, xmlns]
        @@name_xmlns_classes[[name, xmlns]]
      elsif @@name_xmlns_classes.has_key? [name, nil]
        @@name_xmlns_classes[[name, nil]]
      else
        REXML::Element
      end
    end

    ##
    # Import another REXML::Element descendant to:
    # * Either an element class that registered with
    #   name and xmlns before
    # * Or if none was found to the class itself
    #   (you may call this class method on a deriving class)
    def self.import(element)
      klass = class_for_name_xmlns(element.name, element.namespace)
      if klass != self and klass.ancestors.include?(self)
        klass.new.import(element)
      else
        self.new.import(element)
      end
    end

    ##
    # Initialize this element, which will then be initialized
    # with the name registered with name_xmlns.
    def initialize(*arg)
      if arg.empty?
        name, xmlns = self.class::name_xmlns_for_class(self.class)
        super(name)
        if self.class::force_xmlns?
          add_namespace(xmlns)
        end
      else
        super
      end
    end

    ##
    # Add a child element which will be imported according to the
    # child's name and xmlns
    # element:: [REXML::Element] Child
    # result:: [REXML::Element or descendant of XMPPElement] New child
    def typed_add(element)
      if element.kind_of? REXML::Element
        element_ns = (element.namespace.to_s == '') ? namespace : element.namespace

        klass = XMPPElement::class_for_name_xmlns(element.name, element_ns)
        if klass != element.class
          element = klass.import(element)
        end
      end

      super(element)
    end

    def parent=(new_parent)
      if parent and parent.namespace('') == namespace('') and attributes['xmlns'].nil?
        add_namespace parent.namespace('')
      end

      super

      if new_parent and new_parent.namespace('') == namespace('')
        delete_namespace
      end
    end

    def clone
      cloned = self.class.new
      cloned.add_attributes self.attributes.clone
      cloned.context = @context
      cloned
    end

    ##
    # Generic XML attribute 'xml:lang'
    # (REXML provides no shortcut)
    def xml_lang
      attributes['xml:lang']
    end

    ##
    # Set XML language attribute
    def xml_lang=(l)
      attributes['xml:lang'] = l
    end

    ##
    # Set XML language attribute (chainable)
    def set_xml_lang(l)
      self.xml_lang = l
      self
    end

  end
end