# # Metadata.pm: Adds additional properties to a GApi tree. # # Author: Rachel Hestilow # # 2002 Rachel Hestilow ############################################################## package GAPI::Metadata; use XML::LibXML; sub new { my $namespace = $_[1]; my $file = "$namespace.metadata"; my $self = {}; @{$self->{rules}} = (); $self->{metadata} = $namespace; bless $self; $self->load ($file); return $self; } sub parseClass { my ($node, $classes) = @_; my %methods = (); my %signals = (); my @attrs = $node->attributes; my $class_name = $attrs[0]->value; ${$classes}{$class_name} = [\%methods, \%signals]; for ($method_node = $node->firstChild; $method_node != undef; $method_node = $method_node->nextSibling ()) { if ($method_node->nodeName eq "method" or $method_node->nodeName eq "constructor") { $methods{$method_node->firstChild->nodeValue} = 1; } elsif ($method_node->nodeName eq "signal") { $signals{$method_node->firstChild->nodeValue} = 1; } } } sub parseData { my $node = $_[0]; my @data = (); for ($data_node = $node->firstChild; $data_node != undef; $data_node = $data_node->nextSibling ()) { next if $data_node->nodeName ne "attribute"; my @attrs = $data_node->attributes; my $target = $attrs[0]->value; my ($filter_level, $filter_value, $attr_name, $attr_value); my $directives = ""; for ($attr_node = $data_node->firstChild; $attr_node != undef; $attr_node = $attr_node->nextSibling ()) { if ($attr_node->nodeName eq "filter") { my @filter_attrs = $attr_node->attributes; $filter_level = $filter_attrs[0]->value; $filter_value = $attr_node->firstChild->nodeValue; } elsif ($attr_node->nodeName eq "name") { $attr_name = $attr_node->firstChild->nodeValue; } elsif ($attr_node->nodeName eq "value") { $attr_value = $attr_node->firstChild->nodeValue; } elsif ($attr_node->nodeName eq "delete") { $directives = "delete"; } } my @data_attr = ("attribute", $target, "filter", $filter_level, $filter_value, $attr_name, $attr_value, $directives); push @data, \@data_attr; } return @data; } sub load { my ($self, $file) = @_; my $parser = new XML::LibXML; my $doc = $parser->parse_file($file); my $root = $doc->documentElement; for ($rule_node = $root->firstChild; $rule_node != undef; $rule_node = $rule_node->nextSibling ()) { next if $rule_node->nodeName ne "rule"; my %classes = (); my @data; for ($node = $rule_node->firstChild; $node != undef; $node = $node->nextSibling ()) { if ($node->nodeName eq "class") { parseClass ($node, \%classes); } elsif ($node->nodeName eq "data") { @data = parseData ($node); } } push @{$self->{rules}}, [\%classes, \@data]; } } sub fixupParams { my ($method_node, $data_list_ref) = @_; my ($params_node, $node); foreach $data (@$data_list_ref) { if ($$data[1] eq "method" or $$data[1] eq "signal") { $method_node->setAttribute ($$data[5], $$data[6]); next; } for ($node = $method_node->firstChild; $node; $node = $node->nextSibling ()) { if ($node->nodeName eq "parameters") { $params_node = $node; last; } elsif ($node->nodeName eq "return-type" and $$data[1] eq "return") { $node->setAttribute ($$data[5], $$data[6]); last; } } next if not $params_node; if ($$data[1] eq "parameters") { $params_node->setAttribute ($$data[5], $$data[6]); next; } for ($node = $params_node->firstChild; $node; $node = $node->nextSibling ()) { my $param_type; my $param_name; foreach $attr ($node->attributes) { if ($attr->name eq "type") { $param_type = $attr->value; } elsif ($attr->name eq "name") { $param_name = $attr->value; } } if ($$data[3] eq "type") { if ($param_type eq $$data[4]) { $node->setAttribute ($$data[5], $$data[6]); } } elsif ($$data[3] eq "name") { if ($param_name eq $$data[4]) { $node->setAttribute ($$data[5], $$data[6]); } } } } } sub myGetChildrenByTagName { my ($node, $tagname) = @_; my ($child); my (@nodes) = (); for ($child = $node->firstChild; $child; $child = $child->nextSibling ()) { if ($child->nodeName eq $tagname) { push @nodes, $child; } } return @nodes; } sub addClassData { my ($doc, $node, $class, $data_list_ref) = @_; foreach $data (@$data_list_ref) { if ($$data[1] eq "class") { if ($$data[7] eq "delete") { my $parent = $node->parentNode; $parent->removeChild ($node); return; } # my copy of XML::LibXML doesn't have this method. #my @nodes = $node->getChildrenByTagName ($$data[5]); my @nodes = myGetChildrenByTagName ($node, $$data[5]); if (0 == scalar @nodes) { $node->setAttribute ($$data[5], $$data[6]); } next; } } } sub fixupNamespace { my ($self, $doc, $ns_node) = @_; my $node; foreach $rule (@{$self->{rules}}) { my ($classes_ref, $data_list_ref) = @$rule; for ($node = $ns_node->firstChild; $node; $node = $node->nextSibling ()) { next if not ($node->nodeName eq "object" or $node->nodeName eq "interface" or $node->nodeName eq "struct" or $node->nodeName eq "boxed"); my $class, $methods_ref, $attr; foreach $attr ($node->attributes) { if ($attr->name eq "cname") { $class = $attr->value; last; } } my %classes = %$classes_ref; $methods_ref = $classes{$class}[0]; $signals_ref = $classes{$class}[1]; if ({%$classes_ref}->{$class}) { addClassData ($doc, $node, $class, $data_list_ref); } next if not ($methods_ref or $signals_ref); for ($method_node = $node->firstChild; $method_node; $method_node = $method_node->nextSibling ()) { next if not ($method_node->nodeName eq "method" or $method_node->nodeName eq "constructor"); my $method; foreach $attr ($method_node->attributes) { if (($attr->name eq "name" and $method_node->nodeName eq "method") or ($attr->name eq "cname" and $method_node->nodeName eq "constructor")) { $method = $attr->value; last; } } next if not ${%$methods_ref}{$method}; fixupParams ($method_node, $data_list_ref); } for ($signal_node = $node->firstChild; $signal_node; $signal_node = $signal_node->nextSibling ()) { next if $signal_node->nodeName ne "signal"; my $signal; foreach $attr ($signal_node->attributes) { if ($attr->name eq "name") { $signal = $attr->value; last; } } next if not ${$signals_ref}{$signal}; fixupParams ($signal_node, $data_list_ref); } } } } sub fixup { my $doc = $_[0]; my ($api_node, $ns_node); my $metadata = undef; $api_node = $doc->documentElement; return if not ($api_node and $api_node->nodeName eq "api"); for ($ns_node = $api_node->firstChild; $ns_node; $ns_node = $ns_node->nextSibling ()) { next if $ns_node->nodeName ne "namespace"; next if not $ns_node->attributes; my @attrs = $ns_node->attributes; next if not @attrs; my $namespace = $attrs[0]->value; if (-f "$namespace.metadata") { if (not ($metadata and $metadata->{namespace} eq $namespace)) { $metadata = new GAPI::Metadata ($namespace); } $metadata->fixupNamespace ($doc, $ns_node); } } } sub output { my $self = $_[0]; $rule_num = 0; foreach $rule (@{$self->{rules}}) { print "Rule #$rule_num:\n"; my ($classes_ref, $data_list_ref) = @$rule; my %classes = %$classes_ref; my @data_list = @$data_list_ref; foreach $class (keys (%classes)) { print "\tClass $class:\n"; foreach $method (keys %{$classes{$class}}) { print "\t\tMethod $method\n"; } } print "\tData:\n"; foreach $data (@data_list) { printf "\t\tAdd an %s to all %s of %s %s: %s=%s\n", $$data[0], $$data[1], $$data[3], $$data[4], $$data[5], $$data[6]; } $rule_num++; } } 1;