Net-DBus
view release on metacpan or search on metacpan
lib/Net/DBus/Binding/Introspector.pm view on Meta::CPAN
&Net::DBus::Binding::Message::TYPE_UNIX_FD => "unixfd",
);
our %magic_type_map = (
"caller" => sub {
my $msg = shift;
return $msg->get_sender;
},
"serial" => sub {
my $msg = shift;
return $msg->get_serial;
},
);
our %compound_type_map = (
"array" => &Net::DBus::Binding::Message::TYPE_ARRAY,
"struct" => &Net::DBus::Binding::Message::TYPE_STRUCT,
"dict" => &Net::DBus::Binding::Message::TYPE_DICT_ENTRY,
"variant" => &Net::DBus::Binding::Message::TYPE_VARIANT,
);
=item my $ins = Net::DBus::Binding::Introspector->new(object_path => $object_path,
xml => $xml);
Creates a new introspection data manager for the object registered
at the path specified for the C<object_path> parameter. The optional
C<xml> parameter can be used to pre-load the manager with introspection
metadata from an XML document.
=cut
sub new {
my $proto = shift;
my $class = ref($proto) || $proto;
my $self = {};
my %params = @_;
$self->{interfaces} = {};
bless $self, $class;
if (defined $params{xml}) {
$self->{object_path} = exists $params{object_path} ? $params{object_path} : undef;
$self->_parse($params{xml});
} elsif (defined $params{node}) {
$self->{object_path} = exists $params{object_path} ? $params{object_path} : undef;
$self->_parse_node($params{node});
} else {
$self->{object_path} = exists $params{object_path} ? $params{object_path} : undef;
$self->{interfaces} = $params{interfaces} if exists $params{interfaces};
$self->{children} = exists $params{children} ? $params{children} : [];
}
$self->{strict} = exists $params{strict} ? $params{strict} : 0;
# Some versions of dbus failed to include signals in introspection data
# so this code adds them, letting us keep compatability with old versions
if (defined $self->{object_path} &&
$self->{object_path} eq "/org/freedesktop/DBus") {
if (!$self->has_signal("NameOwnerChanged")) {
$self->add_signal("NameOwnerChanged", ["string","string","string"], "org.freedesktop.DBus");
}
if (!$self->has_signal("NameLost")) {
$self->add_signal("NameLost", ["string"], "org.freedesktop.DBus");
}
if (!$self->has_signal("NameAcquired")) {
$self->add_signal("NameAcquired", ["string"], "org.freedesktop.DBus");
}
}
return $self;
}
=item $ins->add_interface($name)
Register the object as providing an interface with the name C<$name>
=cut
sub add_interface {
my $self = shift;
my $name = shift;
$self->{interfaces}->{$name} = {
methods => {},
signals => {},
props => {},
} unless exists $self->{interfaces}->{$name};
}
=item my $bool = $ins->has_interface($name)
Return a true value if the object is registered as providing
an interface with the name C<$name>; returns false otherwise.
=cut
sub has_interface {
my $self = shift;
my $name = shift;
return exists $self->{interfaces}->{$name} ? 1 : 0;
}
=item my @interfaces = $ins->has_method($name, [$interface])
Return a list of all interfaces provided by the object, which
contain a method called C<$name>. This may be an empty list.
The optional C<$interface> parameter can restrict the check to
just that one interface.
=cut
sub has_method {
my $self = shift;
my $name = shift;
if (@_) {
my $interface = shift;
return () unless exists $self->{interfaces}->{$interface};
return () unless exists $self->{interfaces}->{$interface}->{methods}->{$name};
return ($interface);
} else {
my @interfaces;
foreach my $interface (keys %{$self->{interfaces}}) {
if (exists $self->{interfaces}->{$interface}->{methods}->{$name}) {
push @interfaces, $interface;
lib/Net/DBus/Binding/Introspector.pm view on Meta::CPAN
push @{$self->{children}}, $child->att("name");
} else {
push @{$self->{children}}, $self->new(node => $child);
}
}
}
sub _parse_interface {
my $self = shift;
my $node = shift;
my $name = $node->att("name");
$self->{interfaces}->{$name} = {
methods => {},
signals => {},
props => {},
};
foreach my $child ($node->children("method")) {
$self->_parse_method($child, $name);
}
foreach my $child ($node->children("signal")) {
$self->_parse_signal($child, $name);
}
foreach my $child ($node->children("property")) {
$self->_parse_property($child, $name);
}
}
sub _parse_method {
my $self = shift;
my $node = shift;
my $interface = shift;
my $name = $node->att("name");
my @params;
my @returns;
my @paramnames;
my @returnnames;
my $deprecated = 0;
my $no_reply = 0;
foreach my $child ($node->children("arg")) {
my $type = $child->att("type");
my $direction = $child->att("direction");
my $name = $child->att("name");
my @sig = split //, $type;
my @type = $self->_parse_type(\@sig);
if (!defined $direction || $direction eq "in") {
push @params, @type;
push @paramnames, $name;
} elsif ($direction eq "out") {
push @returns, @type;
push @returnnames, $name;
}
}
foreach my $child ($node->children("annotation")) {
my $name = $child->att("name");
my $value = $child->att("value");
if ($name eq "org.freedesktop.DBus.Deprecated") {
$deprecated = 1 if lc($value) eq "true";
} elsif ($name eq "org.freedesktop.DBus.Method.NoReply") {
$no_reply = 1 if lc($value) eq "true";
}
}
$self->{interfaces}->{$interface}->{methods}->{$name} = {
params => \@params,
returns => \@returns,
no_reply => $no_reply,
deprecated => $deprecated,
paramnames => \@paramnames,
returnnames => \@returnnames,
}
}
sub _parse_type {
my $self = shift;
my $sig = shift;
my $root = [];
my $current = $root;
my @cont;
while (my $type = shift @{$sig}) {
if (exists $simple_type_rev_map{ord($type)}) {
push @{$current}, $simple_type_rev_map{ord($type)};
while ($current->[0] eq "array") {
$current = pop @cont;
}
} else {
if ($type eq "(") {
my $new = ["struct"];
push @{$current}, $new;
push @cont, $current;
$current = $new;
} elsif ($type eq "a") {
my $new = ["array"];
push @cont, $current;
push @{$current}, $new;
$current = $new;
} elsif ($type eq "{") {
if ($current->[0] ne "array") {
die "dict must only occur within an array";
}
$current->[0] = "dict";
} elsif ($type eq ")") {
die "unexpected end of struct" unless
$current->[0] eq "struct";
$current = pop @cont;
while ($current->[0] eq "array") {
$current = pop @cont;
}
} elsif ($type eq "}") {
die "unexpected end of dict" unless
$current->[0] eq "dict";
$current = pop @cont;
while ($current->[0] eq "array") {
$current = pop @cont;
}
} elsif ($type eq "v") {
push @{$current}, ["variant"];
while ($current->[0] eq "array") {
$current = pop @cont;
}
} else {
die "unknown type sig '$type'";
}
}
}
return @{$root};
}
sub _parse_signal {
my $self = shift;
my $node = shift;
my $interface = shift;
my $name = $node->att("name");
my @params;
my @paramnames;
my $deprecated = 0;
foreach my $child ($node->children("arg")) {
my $type = $child->att("type");
my $name = $child->att("name");
my @sig = split //, $type;
my @type = $self->_parse_type(\@sig);
push @params, @type;
push @paramnames, $name;
}
foreach my $child ($node->children("annotation")) {
my $name = $child->att("name");
my $value = $child->att("value");
if ($name eq "org.freedesktop.DBus.Deprecated") {
$deprecated = 1 if lc($value) eq "true";
}
}
$self->{interfaces}->{$interface}->{signals}->{$name} = {
params => \@params,
paramnames => \@paramnames,
deprecated => $deprecated,
};
}
sub _parse_property {
my $self = shift;
my $node = shift;
my $interface = shift;
my $name = $node->att("name");
my $access = $node->att("access");
my $deprecated = 0;
foreach my $child ($node->children("annotation")) {
my $name = $child->att("name");
my $value = $child->att("value");
if ($name eq "org.freedesktop.DBus.Deprecated") {
$deprecated = 1 if lc($value) eq "true";
}
}
my @sig = split //, $node->att("type");
$self->{interfaces}->{$interface}->{props}->{$name} = {
type => $self->_parse_type(\@sig),
access => $access,
deprecated => $deprecated,
};
}
=item my $xml = $ins->format([$obj])
Return a string containing an XML document representing the
state of the introspection data. The optional C<$obj> parameter
can be an instance of L<Net::DBus::Object> to include object
specific information in the XML (eg child nodes).
=cut
sub format {
my $self = shift;
my $obj = shift;
my $xml = '<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"' . "\n";
$xml .= '"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">' . "\n";
return $xml . $self->to_xml("", $obj);
}
=item my $xml_fragment = $ins->to_xml
Returns a string containing an XML fragment representing the
state of the introspection data. This is basically the same
as the C<format> method, but without the leading doctype
declaration.
=cut
sub to_xml {
my $self = shift;
my $indent = shift;
my $obj = shift;
my $xml = '';
my $path = $obj ? $obj->get_object_path : $self->{object_path};
unless (defined $path) {
die "no object_path for introspector, and no object supplied";
}
$xml .= $indent . '<node name="' . $path . '">' . "\n";
foreach my $name (sort { $a cmp $b } keys %{$self->{interfaces}}) {
my $interface = $self->{interfaces}->{$name};
$xml .= $indent . ' <interface name="' . $name . '">' . "\n";
foreach my $mname (sort { $a cmp $b } keys %{$interface->{methods}}) {
my $method = $interface->{methods}->{$mname};
$xml .= $indent . ' <method name="' . $mname . '">' . "\n";
my @paramnames = map{ $_ ? "name=\"$_\" " : '' } ( @{$method->{paramnames}} );
my @returnnames = map{ $_ ? "name=\"$_\" " : '' } ( @{$method->{returnnames}} );
foreach my $type (@{$method->{params}}) {
next if ! ref($type) && exists $magic_type_map{$type};
$xml .= $indent . ' <arg ' . (@paramnames ? shift(@paramnames) : "")
. 'type="' . $self->to_xml_type($type) . '" direction="in"/>' . "\n";
}
foreach my $type (@{$method->{returns}}) {
next if ! ref($type) && exists $magic_type_map{$type};
$xml .= $indent . ' <arg ' . (@returnnames ? shift(@returnnames) : "")
. 'type="' . $self->to_xml_type($type) . '" direction="out"/>' . "\n";
}
if ($method->{deprecated}) {
$xml .= $indent . ' <annotation name="org.freedesktop.DBus.Deprecated" value="true"/>' . "\n";
}
if ($method->{no_reply}) {
$xml .= $indent . ' <annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>' . "\n";
}
$xml .= $indent . ' </method>' . "\n";
}
foreach my $sname (sort { $a cmp $b } keys %{$interface->{signals}}) {
my $signal = $interface->{signals}->{$sname};
$xml .= $indent . ' <signal name="' . $sname . '">' . "\n";
my @paramnames = map{ $_ ? "name=\"$_\" " : '' } ( @{$signal->{paramnames}} );
foreach my $type (@{$signal->{params}}) {
next if ! ref($type) && exists $magic_type_map{$type};
$xml .= $indent . ' <arg ' . (@paramnames ? shift(@paramnames) : "")
. 'type="' . $self->to_xml_type($type) . '"/>' . "\n";
}
if ($signal->{deprecated}) {
$xml .= $indent . ' <annotation name="org.freedesktop.DBus.Deprecated" value="true"/>' . "\n";
}
$xml .= $indent . ' </signal>' . "\n";
}
foreach my $pname (sort { $a cmp $b } keys %{$interface->{props}}) {
my $prop = $interface->{props}->{$pname};
my $type = $interface->{props}->{$pname}->{type};
my $access = $interface->{props}->{$pname}->{access};
if ($prop->{deprecated}) {
$xml .= $indent . ' <property name="' . $pname . '" type="' .
$self->to_xml_type($type) . '" access="' . $access . '">' . "\n";
$xml .= $indent . ' <annotation name="org.freedesktop.DBus.Deprecated" value="true"/>' . "\n";
$xml .= $indent . ' </property>' . "\n";
} else {
$xml .= $indent . ' <property name="' . $pname . '" type="' .
$self->to_xml_type($type) . '" access="' . $access . '"/>' . "\n";
}
}
$xml .= $indent . ' </interface>' . "\n";
}
#
# Interfaces don't have children, objects do
#
if ($obj) {
foreach ( $obj->_get_sub_nodes ) {
$xml .= $indent . ' <node name="' . $_ . '"/>' . "\n";
}
} else {
foreach my $child (@{$self->{children}}) {
if (ref($child) eq __PACKAGE__) {
$xml .= $child->to_xml($indent . " ");
} else {
$xml .= $indent . ' <node name="' . $child . '"/>' . "\n";
}
}
}
$xml .= $indent . "</node>\n";
}
=item $type = $ins->to_xml_type($type)
Takes a text-based representation of a data type and returns
the compact representation used in XML introspection data.
=cut
sub to_xml_type {
my $self = shift;
my $type = shift;
my $sig = '';
if (ref($type) eq "ARRAY") {
if ($type->[0] eq "array") {
if ($#{$type} != 1) {
die "array spec must contain only 1 type";
}
$sig .= chr($compound_type_map{$type->[0]});
$sig .= $self->to_xml_type($type->[1]);
} elsif ($type->[0] eq "struct") {
$sig .= "(";
for (my $i = 1 ; $i <= $#{$type} ; $i++) {
$sig .= $self->to_xml_type($type->[$i]);
}
$sig .= ")";
} elsif ($type->[0] eq "dict") {
if ($#{$type} != 2) {
die "dict spec must contain only 2 types";
}
$sig .= chr($compound_type_map{"array"});
( run in 0.549 second using v1.01-cache-2.11-cpan-39bf76dae61 )