2
###############################################################################
3
# japiohtml - Convert japicompat output to pretty html format.
4
# Copyright (C) 2000,2002,2003,2004,2005 Stuart Ballard <stuart.a.ballard@gmail.com>
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of the GNU General Public License
8
# as published by the Free Software Foundation; either version 2
9
# of the License, or (at your option) any later version.
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
# GNU General Public License for more details.
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
###############################################################################
21
## GLOBAL VARIABLES ##
23
# Some global variables used for displaying stuff.
27
# Would be nice not to hardcode this - maybe later...
28
@things = ("package", "class", "interface", "field", "method", "constructor");
30
$javatypes = {Z=>'boolean', B=>'byte', C=>'char', D=>'double', F=>'float',
31
I=>'int', J=>'long', S=>'short', V=>'void'};
36
sub readable_member($);
42
($filever, $origfile, $newfile) = ($1, $2, $3)
43
if $verline =~ /^%\%japio ([^ ]+) ([^ ]+) ([^ ]+)(?: .*)?$/;
44
unless (defined $filever) {
46
This does not look like a japio file.
50
if ($filever ne $japiover) {
52
This japio file claims to be version $filever, but this version of japiohtml
53
only supports version $japiover.
58
($orig,$origdate) = ($1, $2)
59
if $origfile =~ /^([^@]*?)(?:\.japi(?:\.gz)?)?(?:@(.*))?$/;
60
($new, $newdate) = ($1, $2)
61
if $newfile =~ /^([^@]*?)(?:\.japi(?:\.gz)?)?(?:@(.*))?$/;
62
$origdate =~ s/_/ /g if $origdate;
63
$newdate =~ s/_/ /g if $newdate;
65
my $date = gmtime() . " GMT";
68
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
69
"http://www.w3.org/TR/html4/loose.dtd">
72
<title>Results of comparison between $orig and $new</title>
73
<link rel="stylesheet" type="text/css" href="japi.css">
76
<h1>Results of comparison between $orig and $new</h1>
77
<p class="datestamp">Comparison run at $date</p>
79
print <<EOF if $origdate;
80
<p class="datestamp">$orig API scanned at $origdate</p>
82
print <<EOF if $newdate;
83
<p class="datestamp">$new API scanned at $newdate</p>
92
if (/^notify (.*)$/) {
93
print "<p class='notify'>$1</p>\n";
94
} elsif (/^categories (.*)$/) {
96
<table class="legend" align="right" border="0" cellspacing="0" cellpadding="3">
99
<td>All correct -</td>
101
for (my $i = 100; $i >= 0; $i -= 10) {
103
<td class="ok-${i}pct"> </td>
107
<td class="ok-none"> </td>
108
<td>- None correct</td>
112
<table class="summary" border="0" cellspacing="0" cellpadding="3">
116
my @bits = split / /, $1;
117
foreach my $bit (@bits) {
118
if ($bit =~ /^(=?)(.)(.*)/) {
120
my $capegory = uc($2).$3;
123
$letter = $1 if $cat =~ /_(.)/;
125
$letters->{$cat} = $letter;
127
push @categories, $cat;
128
$okcats->{$cat} = 1 if $ok;
131
<th class="$cat">$capegory</th>
138
} elsif (/^summary (.*)$/) {
139
die "No categories line found before summary line" unless @categories;
140
my @bits = split / /, $1;
141
my ($extra, $total, $values, $etotal, $oktotal, $nonmoototal) = ({}, 0, {}, 0, 0, 0);
142
my $pkg = shift @bits;
143
foreach my $bit (@bits) {
144
my ($plus, $key, $value, $smootval, $mootval) = ($1, $2, $3, $4, $5)
145
if $bit =~ /^(\+?)([^:+]+):([0-9]+)\^([0-9]+)>([0-9]+)$/;
146
$etotal += $value - $smootval - $mootval;
147
$oktotal += $value - $smootval - $mootval if $okcats->{$key};
149
$extra->{$key} = $value - $smootval - $mootval;
151
$total += $value - $smootval;
152
$nonmoototal += $value - $smootval - $mootval;
153
$values->{$key} = $value - $smootval - $mootval;
154
$values->{MOOT} += $mootval;
162
$pkga = $pkgn = "Total";
163
} elsif ($pkg =~ /^#(.)(.*)$/) {
165
$pkgn .= "e" if $pkgn =~ /[sx]$/;
169
$pkgl = $pkg; $pkgl =~ s/\./_/g;
171
$pkga =~ s/\./.<span class="sp"> <\/span>/g;
172
$pkga = "<a href=\"#pkg_$pkgl\">$pkga</a>";
173
push @packages, $pkg;
175
my $pctclass = "none";
177
$pctclass = (int($oktotal * 10 / $etotal) * 10)."pct" if $oktotal;
182
<tr class="alternating-$alt">
183
<th class="ok-$pctclass">$pkga:</th>
186
foreach my $key (@categories) {
188
if ($values->{$key}) {
189
$val = (int($values->{$key} * 10000 / $total)/100) . "%";
190
} elsif ($extra->{$key}) {
191
$val = (int($extra->{$key} * 10000 / $total)/100) . "%";
194
if ($pkg ne "#" && $key ne "good") {
195
my $errl = $key; $errl =~ s/\./_/g;
196
$val = "<a href=\"#err_${errl}_$pkgl\">$val</a>";
201
my $class = $key; $class =~ s/\./-/g;
203
<td class="$class" title="$pkgn $key">$val</td>
209
# Now we need to integerize the percentages so we can emit a table whose
210
# width (minus the "extra" bit) adds up to exactly 100 pixels. We also want
211
# to ensure that *anything* nonzero gets at least a pixel. See the file
212
# design/percent-rounding.txt for a justification of this algorithm.
214
# Start off assuming nothing's less than 1%, and iterate until we find
215
# that we're right. m is the number of adjustable (<1%) items, and t is
216
# the total of the non-adjustable items. Lastm keeps track of what we
217
# thought m was last time round. We loop until we do an entire pass without
218
# m ending up different from lastm. Lastm starts off as -1 just so that the
219
# m == lastm test fails first time around.
224
until ($m == $lastm) {
227
# Loop over the items that haven't already been marked adjustable. For
228
# each such item, determine whether it needs to be adjustable based on
229
# the current values of m and t. If it does, mark it as adjustable and
230
# update m and t accordingly.
231
foreach my $item (keys %$values) {
232
if ($values->{$item} > 0) {
233
if (!$adjustable->{$item}) {
234
if ($values->{$item} * (100-$m) < $t) {
235
$t -= $values->{$item};
237
$adjustable->{$item} = 1;
244
# Having calculated the final values of m and t, and also knowing exactly
245
# which items are adjustable, we can now calculate the adjusted totals.
246
# Non-adjustable items are scaled up by a constant factor; adjustable
247
# items are all set to exactly 1% of the scaled total.
248
my $adjtotal = 100 * $t;
250
foreach my $item (keys %$values) {
251
if ($values->{$item} > 0) {
252
if ($adjustable->{$item}) {
253
$adjvalue->{$item} = $t;
255
$adjvalue->{$item} = $values->{$item} * (100-$m);
260
# Calculate the percentage rounded *down* to the nearest integer, and also
261
# calculate the magnitude of the difference between the integer percentage
262
# and the actual percentage. This is still all done in integer math...
263
# While we're at it, sum the percentages so we can see how close we got,
268
foreach my $item (keys %$values) {
269
if ($values->{$item} > 0) {
270
$percent->{$item} = ($adjvalue->{$item} * 100) / $adjtotal;
271
$diff->{$item} = $adjvalue->{$item} * 100 - $percent->{$item} * $adjtotal;
272
$totalpct += $percent->{$item};
278
# Find the items with the largest differences, and adjust them upwards,
279
# until 100% is reached. No need to reset the difference since we're looping
280
# through the items and will never repeat: it's easy to show that the upper
281
# bound on the number of upwards adjustments needed is smaller than the
283
foreach my $item (sort { $diff->{$b} <=> $diff->{$a} } keys %$values) {
284
if ($values->{$item} > 0) {
285
last if ($totalpct >= 100);
291
foreach my $item (keys %$extra) {
292
if ($extra->{$item} > 0) {
293
$percent->{$item} = ($extra->{$item} * 1000 + 5) / ($total * 10);
294
$percent->{$item} = 1 unless $percent->{$item};
295
$totalpct += $percent->{$item};
301
<table width="$totalpct" cellpadding="0" cellspacing="0" class="bar">
304
foreach my $item (@categories) {
305
if (exists $percent->{$item}) {
306
my $pct = $percent->{$item};
307
my $class = $item; $class =~ s/\./-/g;
308
my $altc = uc($letters->{$item});
309
my $alt = $altc x ($percent->{$item} / 5);
310
$alt = $altc unless $alt;
312
<td width="$pct" class="$class-bar" title="$pkgn $item">
313
<img src="1x1.gif" width="1" height="12" alt="$alt">
324
} elsif (/^error (.*)$/) {
326
my ($etype, $isa, $item, $sups, $rest) = split / /, $line, 5;
327
my ($pkg, $cmember) = split /,/, $item;
328
push @{$errors->{"$pkg/$etype"}}, $line;
329
$totals->{"$etype/$isa"}++;
330
$totals->{"$etype/$isa/$pkg"}++;
331
} elsif (/^end japio$/) {
334
die "Line not understood in japio file:\n$_";
340
<table class="phead" width="100%">
346
foreach my $cat (@categories) {
347
next if $cat eq "good";
348
my $cap = uc($1).$2 if $cat =~ /^(.)(.*)$/;
349
my $catn = $cat; $catn =~ s/\./-/g;
351
foreach my $thing (@things) {
352
my $tot = $totals->{"$cat/$thing"};
356
$th .= "e" if $th =~ /[sx]$/;
359
$vals .= "," if $vals;
360
$vals .= " $tot $th";
363
$vals = " None" unless $vals;
365
<span class="$catn">$cap:$vals.</span>
374
foreach my $pkg (@packages) {
375
my $pkgl = $pkg; $pkgl =~ s/\./_/g;
378
foreach my $cat (@categories) {
379
my $catl = $cat; $catl =~ s/\./_/g;
380
unless ($cat eq "good") {
381
if ($errors->{"$pkg/$cat"}) {
384
$catlinks .= "<a name=\"err_${catl}_$pkgl\"></a>";
390
<table class="phead" width="100%">
393
<h3><a name="pkg_$pkgl"></a>$catlinks$pkg</h3>
396
foreach my $cat (@categories) {
397
next if $cat eq "good";
398
my $cap = uc($1).$2 if $cat =~ /^(.)(.*)$/;
399
my $catn = $cat; $catn =~ s/\./-/g;
400
my $catl = $cat; $catl =~ s/\./_/g;
402
foreach my $thing (@things) {
403
my $tot = $totals->{"$cat/$thing/$pkg"};
407
$th .= "e" if $th =~ /[sx]$/;
410
$vals .= "," if $vals;
411
$vals .= " $tot $th";
414
print <<EOF if $vals;
415
<a class="$catn" href="#err_${catl}_$pkgl">$cap:$vals.</a>
425
<a name="pkg_$pkgl"></a>$catlinks
428
foreach my $cat (@categories) {
429
if ($errors->{"$pkg/$cat"}) {
430
my $catl = $cat; $catl =~ s/\./_/g;
431
my $catc = $cat; $catc =~ s/\./-/g;
432
my $cap = uc($1).$2 if $cat =~ /^(.)(.*)$/;
434
<h4 class="ehead"><a name="err_${catl}_$pkgl"></a>$cap</h4>
437
foreach my $line (@{$errors->{"$pkg/$cat"}}) {
438
my ($etype, $isa, $item, $sups, $rest) = split / /, $line, 5;
439
my ($was, $is) = split /\//, $rest;
440
$was =~ s/~s/\//g; $was =~ s/~t/~/g;
441
$is =~ s/~s/\//g; $is =~ s/~t/~/g;
442
my $msg = $was ? "$was in $orig, but" : "";
443
my $ritem = readable_item($item);
444
my $outline = htmlencode("$isa $ritem: $msg $is in $new");
468
sub readable_item($) {
470
my ($fqclass, $member) = split /!/, $item, 2;
471
my ($pkg, $class) = split /,/, $fqclass, 2;
477
$ritem .= "." . readable_member($member) if $member;
481
# Convert all the type signatures in a method name...
482
sub readable_member($) {
484
if ($member =~ /^(.*)\((.*)\)$/) {
485
my ($name, $params) = ($1, $2);
486
$params = sig2typelist($params);
487
$member = "$name($params)";
488
} elsif ($member =~ /^#(.*)$/) {
494
# Convert a type signature as used in a japi file to a displayable type.
497
return sig2type($1) . '[]' if $sig =~ /^\[(.*)$/;
498
return sig2type($1) . "..." if $sig =~ /^\.(.*)$/;
499
return "? super " . sig2type($1) if $sig =~ /^\}(.*)$/;
500
return "?" if $sig eq "{Ljava/lang/Object;";
501
return "? extends " . sig2type($1) if $sig =~ /^\{(.*)$/;
502
return "T" if $sig eq "\@0";
503
return "T" . ($1 + 1) if $sig =~ /^\@([0-9]+)$/;
504
return $javatypes->{$sig} if $javatypes->{$sig};
506
$sig = $1 if $sig =~ /^L(.*);$/;
507
($sig, $gparams) = ($1, $2) if $sig =~ /^([^<>]+)<(.*)>$/;
510
$sig = "$sig<" . sig2typelist($gparams) . ">" if defined($gparams);
513
sub sig2typelist($) {
515
my @sigs = splitgenstr($list);
516
return join(", ", map {sig2type($_)} @sigs);
519
my ($str, $char) = @_;
520
$str =~ s/[^$char]//g;
526
my @items = split(/,/, $str);
530
foreach my $item (@items) {
531
$class .= "," if $class;
533
if (countchar($class, "<") == countchar($class, ">")) {
534
push @result, $class;
538
push @result, $class if $class;
b'\\ No newline at end of file'