/usr/local/CPAN/CPANXR/CPANXR/Apache/Symbol.pm
package CPANXR::Apache::Symbol;
use CPANXR::Apache::Util;
use CPANXR::Database;
use CPANXR::Parser qw(:constants);
use File::Spec::Functions qw(catdir);
use Data::Page;
use strict;
our %Types = (
t1 => [CONN_PACKAGE, 'packages'],
t2 => [CONN_FUNCTION, 'function calls'],
t3 => [CONN_METHOD, 'method calls'],
t3 => [CONN_MACRO, 'macros'],
t4 => [CONN_INCLUDE, 'uses'],
t5 => [CONN_DECL, 'declarations'],
t6 => [CONN_ISA, 'inheritence'],
t7 => [CONN_REF, 'references'],
t8 => [CONN_LINK, 'includes'],
t9 => [CONN_FILE, 'files'],
);
sub find {
my ($self, $r, $q) = @_;
my $symbol_id = $q->param('symbol');
my $pkg_id = $q->param('pkg');
my $current = $q->param('p') || 1;
unless($symbol_id =~ /^\d+$/) {
$r->print("Missing argument <b>symbol</b>");
return;
}
my $pkg = "";
if($pkg_id) {
unless($pkg_id =~ /^\d+$/) {
$r->print("Malformed argument <b>pkg</b>");
}
$pkg = CPANXR::Database->select_symbol($pkg_id)->[0]->[0];
}
my $symbol = CPANXR::Database->select_symbol($symbol_id)->[0]->[0];
$r->print("Looking for <b>$symbol</b>");
if($pkg) {
$r->print(" in package <b>$pkg</b>");
}
$r->print(" and found<br>\n");
$r->print("<blockquote>\n");
my %args;
# Extract limit
{
my @limit_types;
while(my ($param, $value) = each %Types) {
my $v = $q->param($param) ? 1 : 0;
push @limit_types, $value->[0] if $v;
}
if(@limit_types) {
$args{limit_types} = \@limit_types;
}
}
# Select files matching symbol
my $files = CPANXR::Database->select_files(symbol_id => $symbol_id);
@$files = map { [ $symbol_id, $symbol, "", "", $_->[2], $_->[0], undef, CONN_FILE, undef, $_->[1]] } @$files;
# Select connections matching symbol
my $result = CPANXR::Database->select_connections(symbol_id => $symbol_id, %args);
unshift @$result, @$files;
$result = $self->transform($result);
my $table = CPANXR::Apache::Util::Table->new($r, 4, [qw(15% 55% 15% 15%)]);
$table->begin;
$table->header("<b>Type:</b>", "<b>File:</b>", "<b>Location:</b>", "<b>Show:</b>");
my $page = Data::Page->new(scalar @$result, 10, $current);
my ($pre_file, $pre_type) = ("","");
for($page->splice($result)) {
if($pre_file ne $_->[1] || ($pre_type ne $_->[0])) {
$pre_file = $_->[1];
} else {
$_->[1] = "";
}
if($pre_type eq $_->[0]) {
$_->[0] = "";
} else {
$pre_type = $_->[0];
}
$table->print(@$_);
}
$table->end;
# Write navigation
my $base = "find?symbol=$symbol_id&pkg=$pkg_id";
my $base_type = "";
$base_type .= "&$_=" . ($q->param($_) ? 1 : 0) for(keys %Types);
$base_type =~ s/=0/=1/g unless($base =~ /=1/);
CPANXR::Apache::Util->navigator($r, $page, $base . $base_type);
# Limit search
$r->print(qq{<br>
<form action="find" method="post">
<input type="hidden" name="symbol" value="$symbol_id">
<input type="hidden" name="pkg" value="$pkg_id">
<b>Show</b>: });
for(sort { $Types{$a}->[1] cmp $Types{$b}->[1] } keys %Types) {
my $checked = $q->param($_) ? "checked" : "";
$r->print(qq{<input type="checkbox" name="$_" value="1" $checked>@{[lcfirst($Types{$_}->[1])]} });
}
$r->print(q{<input type="submit" value="Redefine search"></form>});
$r->print("</blockquote>");
}
my @Types = ("uses",
"calls",
"calls",
"functions",
"macros",
"packages",
"inherits",
"references",
"includes",
"files",
);
sub transform {
my ($self, $result) = @_;
my $groups = {};
foreach my $item (@$result) {
my $type = $Types[$item->[7]];
$groups->{$type} = {} unless(exists $groups->{$type});
if(exists $groups->{$type}->{$item->[4]}) {
push @{$groups->{$type}->{$item->[4]}->{lines}}, [$item->[2], $item->[0], $item->[6], $item->[8]];
} else {
$groups->{$type}->{$item->[4]} = { file_id => $item->[5],
lines => [[$item->[2], $item->[0], $item->[6], $item->[8]]], };
}
}
my @result;
foreach my $type (sort keys %$groups) {
my $show_type = "<b>$type</b>";
while(my ($file, $entries) = each %{$groups->{$type}}) {
my $show_file = $file;
if(@{$entries->{lines}}) {
foreach my $line (@{$entries->{lines}}) {
my $show_line = "";
if($line->[0] ne "") {
$show_line = qq{<a href="show?id=$entries->{file_id}&hl=$line->[0]#l$line->[0]">at line $line->[0]</a>};
} else {
$show_line = "<i>n/a</i>";
}
my $func = "";
if($type eq 'functions') {
my $pkg_id = $line->[2] || $line->[3];
my $sym_id = $line->[1];
$func = qq{<a href="graph?sub=${sym_id}_${pkg_id}">Call graph</a>};
} elsif($type eq 'packages') {
$func = qq{<a href="graph?class=$line->[1]">IS-A graph</a>};
} elsif($type eq 'files') {
$func = qq{<a href="graph?file=$entries->{file_id}">File graph</a>};
}
push @result, [$show_type, $show_file, $show_line, $func];
}
}
}
}
return \@result;
}
1;