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
|
# dbus/xml.rb - introspection parser, rexml/nokogiri abstraction
#
# This file is part of the ruby-dbus project
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
# Copyright (C) 2012 Geoff Youngs
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License, version 2.1 as published by the Free Software Foundation.
# See the file "COPYING" for the exact licensing terms.
# TODO: check if it is slow, make replaceable
require "rexml/document"
begin
require "nokogiri"
rescue LoadError
end
module DBus
# = D-Bus introspect XML parser class
#
# This class parses introspection XML of an object and constructs a tree
# of Node, Interface, Method, Signal instances.
class IntrospectXMLParser
class << self
attr_accessor :backend
end
# Creates a new parser for XML data in string _xml_.
def initialize(xml)
@xml = xml
end
class AbstractXML
def self.have_nokogiri?
Object.const_defined?("Nokogiri")
end
class Node
def initialize(node)
@node = node
end
# required methods
# returns node attribute value
def [](key); end
# yields child nodes which match xpath of type AbstractXML::Node
def each(xpath); end
end
# required methods
# initialize parser with xml string
def initialize(xml); end
# yields nodes which match xpath of type AbstractXML::Node
def each(xpath); end
end
class NokogiriParser < AbstractXML
class NokogiriNode < AbstractXML::Node
def [](key)
@node[key]
end
def each(path, &block)
@node.search(path).each { |node| block.call NokogiriNode.new(node) }
end
end
def initialize(xml)
@doc = Nokogiri.XML(xml)
end
def each(path, &block)
@doc.search("//#{path}").each { |node| block.call NokogiriNode.new(node) }
end
end
class REXMLParser < AbstractXML
class REXMLNode < AbstractXML::Node
def [](key)
@node.attributes[key]
end
def each(path, &block)
@node.elements.each(path) { |node| block.call REXMLNode.new(node) }
end
end
def initialize(xml)
@doc = REXML::Document.new(xml)
end
def each(path, &block)
@doc.elements.each(path) { |node| block.call REXMLNode.new(node) }
end
end
@backend = if AbstractXML.have_nokogiri?
NokogiriParser
else
REXMLParser
end
# @return [Array(Array<Interface>,Array<String>)]
# a pair: [list of Interfaces, list of direct subnode names]
def parse
# Using a Hash instead of a list helps merge split-up interfaces,
# a quirk observed in ModemManager (I#41).
interfaces = Hash.new do |hash, missing_key|
hash[missing_key] = Interface.new(missing_key)
end
subnodes = []
t = Time.now
d = IntrospectXMLParser.backend.new(@xml)
d.each("node/node") do |e|
subnodes << e["name"]
end
d.each("node/interface") do |e|
i = interfaces[e["name"]]
e.each("method") do |me|
m = Method.new(me["name"])
parse_methsig(me, m)
i << m
end
e.each("signal") do |se|
s = Signal.new(se["name"])
parse_methsig(se, s)
i << s
end
end
d = Time.now - t
if d > 2
DBus.logger.debug "Some XML took more that two secs to parse. Optimize me!"
end
[interfaces.values, subnodes]
end
######################################################################
private
# Parses a method signature XML element _e_ and initialises
# method/signal _m_.
def parse_methsig(e, m)
e.each("arg") do |ae|
name = ae["name"]
dir = ae["direction"]
sig = ae["type"]
if m.is_a?(DBus::Signal)
# Direction can only be "out", ignore it
m.add_fparam(name, sig)
elsif m.is_a?(DBus::Method)
case dir
# This is a method, so dir defaults to "in"
when "in", nil
m.add_fparam(name, sig)
when "out"
m.add_return(name, sig)
end
else
raise NotImplementedError, dir
end
end
end
end # class IntrospectXMLParser
end # module DBus
|