# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://home.gna.org/xmpp4r/ 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