line
stmt
bran
cond
sub
pod
time
code
1
#!/usr/bin/perl
2
#-------------------------------------------------------------------------------
3
# Extract documentation from Java source code.
4
# Philip R Brenan at gmail dot com, Appa Apps Ltd, 2017
5
#-------------------------------------------------------------------------------
6
package Java::Doc;
7
require v5.16.0;
8
1
1
398
use warnings FATAL => qw(all);
1
6
1
29
9
1
1
5
use strict;
1
1
1
18
10
1
1
4
use Carp qw(confess);
1
1
1
51
11
1
1
269
use Data::Dump qw(dump);
1
5655
1
65
12
1
1
587
use Data::Table::Text qw(:all);
1
41420
1
2191
13
14
our $VERSION = '20171011';
15
16
genLValueHashMethods(qw(parse)); # Combined parse tree of all the input files read
17
genLValueHashMethods(qw(classes)); # Classes encountered in all the input files read
18
19
my %veryWellKnownClassesHash = map {$_=>1} split /\n/, &veryWellKnownClasses; # Urls describing some well known Java classes
20
21
sub urlIfKnown($$) # Replace a well known type with an informative url
22
12
12
0
18
{my ($javaDoc, $type) = @_; # Java doc builder, type to replace with an informative url
23
12
38
for my $url(keys %veryWellKnownClassesHash, @{$javaDoc->wellKnownClasses})
12
191
24
198
297
{my $i = index($url, "/$type.html");
25
198
100
269
if ($i >= 0)
26
{#say STDERR "AAAA $i $type $url", ;
27
6
33
return qq($type );
28
}
29
}
30
$type
31
6
25
}
32
33
sub getAttributes($) # Get the attributes from a method type: public, private, final, static etc
34
12
12
0
15
{my ($type) = @_; # Type as parsed out
35
12
22
my $t = qq( $type ); # Blank pad
36
12
24
$t =~ s(\A\s*[\(\{]) ( )gs; # Replace leading open bracket with space
37
12
14
my @attributes; # Method attributes
38
12
16
for(qw(public private protected abstract final static))
39
72
100
421
{push @attributes, $_ if $t =~ m($_)s;
40
72
370
$t =~ s($_) ( )gs;
41
}
42
12
40
$t =~ s(\s+) ( )gs; # Remove excess white space
43
12
26
(trim($t), @attributes)
44
}
45
#say STDERR dump([getAttributes(" (final public static float ")]); exit;
46
47
sub parseJavaFile($$) # Parse java file for package, class, method, parameters
48
1
1
0
2
{my ($javaDoc, $fileOrString) = @_; # Java doc builder, Parse tree, java file or string of java to process
49
1
22
my $parse = $javaDoc->parse; # Parse tree
50
51
1
50
8
my $s = $fileOrString =~ m(\n)s ? $fileOrString : readFile($fileOrString);
52
53
1
8
my $package; # Package we are in
54
my @class; # Containing classes as array
55
1
0
my $class; # Containing classes as a.b.c
56
1
0
my $method; # Method we are in
57
1
2
my $state = 0; # 0 - outer, 1 - parameters to last method
58
59
1
10
for(split /\n/, $s)
60
30
100
55
{if ($state == 0)
50
61
18
100
110
{if (m(\A\s*package\s+((\w+|\.)+))) # 'package' package ;
100
50
100
62
{#say STDERR "Package = $1";
63
1
4
$package = $1;
64
}
65
elsif (m(\A.*?class\s+(\w+)\s*\{?\s*//C\s+(.*?)\s*\Z)) # Class with optional '{' //C
66
2
5
{push @class, $1; # Save containing class
67
2
5
$class = join '.', @class;
68
#say STDERR "Class $class = $1 = $2";
69
2
40
$javaDoc->classes->{$class} = $parse->{$package}{$class}{comment} = $2; # Record the last encountered class as the class to link to - could be improved!
70
}
71
elsif (m(\A\s*}\s*//C\s+(\w+))) # '}' '//C' className - close class className
72
{#say STDERR "Class = $1 End";
73
0
0
0
if (!@class)
74
0
0
{warn "Closing class $1 but no class to close";
75
}
76
0
0
0
if ($1 ne $class[-1])
77
0
0
{warn "Closing class $1 but in class $class ignored";
78
}
79
else
80
0
0
{pop @class;
81
0
0
$class = join '.', @class;
82
}
83
}
84
elsif (m(\A\s*(.*?)\s+(\w+)\s*(\(\))?\s*\(?\s*//(M|c|O=\S+)\s+(.*?)\s*\Z))# Method with either '()' meaning no parameters or optional '(' followed by //M for method, //c for constructor, //O=package.method for override of the named method
85
3
10
{my ($empty, $res, $comment) = ($3, $4, $5);
86
3
7
$method = $2;
87
88
3
6
my ($type, @attributes) = getAttributes($1); # Method attributes
89
90
3
24
my $override; # Method is an override
91
3
50
9
if ($res =~ m(\Ac\Z)s) # In summa it is a constructor
50
92
0
0
{push @attributes, q(constructor); # Constructor
93
0
0
$type = qq($method ); # Type for a constructor is the constructor name
94
}
95
elsif ($res =~ m(\AO=(.+?)\Z)s) # Override
96
0
0
{$override = $1;
97
0
0
$comment = qq(See $comment );
98
}
99
100
3
50
33
14
if ($package and $class) # Save method details is possible
101
{#say STDERR "Method = $method == $type == $comment ";
102
103
3
11
push @{$parse->{$package}{$class}{methods}},
3
11
104
{type=>$javaDoc->urlIfKnown($type), name=>$method,
105
comment=>$comment, attributes=>[@attributes]};
106
3
50
33
14
$state = 1 if !$empty and !$override; # Get parameters next if method has parameters and is not an override
107
}
108
else
109
0
0
{my $m = qq(Ignoring method $method as no preceding);
110
0
0
my $f = qq(in file:\n$fileOrString\nLine:\n$_);
111
0
0
0
warn "$m package $f" unless $package;
112
0
0
0
warn "$m class $f" unless $class;
113
}
114
}
115
}
116
elsif ($state == 1)
117
12
100
72
{if (m(\A.\s*(.+?)\s+(\w+)\s*[,\)\{]*\s*//P\s+(.*?)\s*\Z)) # type name, optional ',){', //P
118
{#say STDERR "Parameter =$1=$2=$3";
119
9
25
my ($type, $parameter, $comment) = ($1, $2, $3);
120
9
16
my ($t, @attributes) = getAttributes($type);
121
9
66
push @{$parse->{$package}{$class}{methods}[-1]{parameters}},
9
29
122
[$javaDoc->urlIfKnown($t), $parameter, $comment, [@attributes]];
123
}
124
else # End of parameters if the line does not match
125
3
5
{$state = 0;
126
3
50
33
16
if ($package and $class and $method)
33
127
3
8
{my $m = $parse->{$package}{$class}{methods}[-1];
128
3
50
5
if (my $p = $m->{parameters})
129
3
50
9
{if (my @p = @$p)
130
3
6
{$m->{nameSig} = join ', ', map {$_->[1]} @p;
9
17
131
3
6
$m->{typeSig} = join ', ', map {$_->[0]} @p;
9
24
132
}
133
}
134
}
135
else
136
0
0
{warn "Ignoring method $method as no preceding package or class or ".
137
"method in file:\n$fileOrString\nLine:\n$_";
138
}
139
}
140
}
141
}
142
$parse
143
1
4
}
144
145
sub parseJavaFiles($) # Parse all the input files into one parse tree
146
1
1
0
2
{my ($javaDoc) = @_; # Java doc processor
147
1
2
for(@{$javaDoc->source}) # Extend the parse tree with the parse of each source file
1
14
148
1
8
{$javaDoc->parseJavaFile($_);
149
}
150
}
151
152
sub htmlJavaFiles($) # Create documentation using html for all java files from combined parse tree
153
1
1
0
3
{my ($javaDoc) = @_; # Java doc processor, combined parse tree
154
1
16
my $parse = $javaDoc->parse; # Parse tree
155
1
50
24
my $indent = $javaDoc->indent // 0; # Indentation per level
156
1
10
my @c = @{$javaDoc->colors}; # Back ground colours
1
14
157
1
50
7
@c = 'white' unless @c;
158
1
2
my $d; # Current background colour - start
159
1
1
my $D = q(); # Current background colour - end
160
my $swapColours = sub # Swap background colour
161
7
7
8
{my ($margin) = @_;
162
7
8
my $m = $margin * $indent;
163
7
11
push @c, my $c = shift @c;
164
7
14
$d = qq();
165
1
6
};
166
1
2
&$swapColours(0); # Swap background colour
167
168
1
3
my @h = <
169
$d
170
171
172
173
174
Packages
175
182
$D
183
END
184
185
1
3
for my $package(sort keys %$parse)
186
1
1
{my %package = %{$parse->{$package}};
1
4
187
1
4
&$swapColours(1);
188
1
7
push @h, <
189
190
$d
191
Classes in package: $package
192
193
Class Description
194
END
195
1
4
for my $class(sort keys %package)
196
2
2
{my %class = %{$package{$class}};
2
9
197
2
3
my $classComment = $class{comment};
198
2
10
push @h, <<"END";
199
$class
200
$classComment
201
202
END
203
}
204
1
3
push @h, <
205
206
$D
207
END
208
1
3
for my $class(sort keys %package)
209
2
2
{my %class = %{$package{$class}};
2
4
210
2
4
my $classComment = $class{comment};
211
2
5
&$swapColours(2);
212
2
8
push @h, <
213
$d
214
215
Methods in class: $class , package: $package
216
$classComment
217
218
Returns Method Signature Attributes Description
219
END
220
2
3
for my $method(@{$class{methods}})
2
4
221
3
4
{my %method = %{$method};
3
11
222
3
50
5
my $attr = join ' ', @{$method{attributes}//[]};
3
8
223
3
4
my $type = $method{type};
224
3
4
my $name = $method{name};
225
3
3
my $comment = $method{comment};
226
3
50
7
my $sig = $method{typeSig} // 'void';
227
3
15
push @h, <
228
$type
229
$name
230
$sig
231
$attr
232
$comment
233
234
END
235
}
236
2
6
push @h, <
237
238
$D
239
END
240
2
3
for my $method(@{$class{methods}})
2
3
241
3
3
{my %method = %{$method};
3
10
242
3
4
my $type = $method{type};
243
3
3
my $name = $method{name};
244
3
4
my $comment = $method{comment};
245
3
7
&$swapColours(3);
246
3
10
push @h, <
247
$d
248
249
$name : $type
250
$comment
251
END
252
253
3
50
7
if (my $parameters = $method{parameters})
254
3
17
{my @parameters = @$parameters;
255
3
5
push @h, <
256
Parameters:
257
258
Name Type Attributes Description
259
END
260
3
13
for my $parameter(@parameters)
261
9
15
{my ($type, $name, $comment, $attributes) = @$parameter;
262
9
50
10
my $attr = join ' ', @{$attributes//[]};
9
17
263
9
28
push @h, qq( $name $type $attr $comment );
264
}
265
3
5
push @h, <
266
267
END
268
}
269
3
10
push @h, <
270
$D
271
272
END
273
}
274
}
275
}
276
277
1
10
s(L<(.+?)>) ($1 )gs for @h;
278
279
@h
280
1
9
}
281
282
1
1
0
8
sub veryWellKnownClasses {<<'END'}
283
https://developer.android.com/reference/android/app/Activity.html
284
https://developer.android.com/reference/android/content/Context.html
285
https://developer.android.com/reference/android/graphics/BitmapFactory.html
286
https://developer.android.com/reference/android/graphics/Bitmap.html
287
https://developer.android.com/reference/android/graphics/Canvas.html
288
https://developer.android.com/reference/android/graphics/drawable/BitmapDrawable.html
289
https://developer.android.com/reference/android/graphics/drawable/Drawable.html
290
https://developer.android.com/reference/android/graphics/Matrix.html
291
https://developer.android.com/reference/android/graphics/Paint.html
292
https://developer.android.com/reference/android/graphics/Path.html
293
https://developer.android.com/reference/android/graphics/PorterDuff.Mode.html
294
https://developer.android.com/reference/android/graphics/RectF.html
295
https://developer.android.com/reference/android/media/MediaPlayer.html
296
https://developer.android.com/reference/android/util/DisplayMetrics.html
297
https://developer.android.com/reference/java/io/ByteArrayOutputStream.html
298
https://developer.android.com/reference/java/io/DataInputStream.html
299
https://developer.android.com/reference/java/io/DataOutputStream.html
300
https://developer.android.com/reference/java/io/File.html
301
https://developer.android.com/reference/java/io/FileOutputStream.html
302
https://developer.android.com/reference/java/lang/String.html
303
https://developer.android.com/reference/java/lang/Thread.html
304
https://developer.android.com/reference/java/util/Stack.html
305
https://developer.android.com/reference/java/util/TreeMap.html
306
https://developer.android.com/reference/java/util/TreeSet.html
307
https://developer.android.com/studio/command-line/adb.html
308
END
309
310
#1 Attributes # Attributes that can be set or retrieved by assignment
311
312
if (1) { # Parameters that can be set by the caller
313
genLValueArrayMethods(qw(source)); # A reference to an array of Java source files that contain documentation as well as java
314
genLValueScalarMethods(qw(target)); # Name of the file to contain the generated documentation
315
genLValueArrayMethods(qw(wellKnownClasses)); # A reference to an array of urls that contain the class name of well known Java classes such as: L which will be used in place of the class name to make it possible to locate definitions of these other classes.
316
genLValueScalarMethods(qw(indent)); # Indentation for methods vs classes and classes vs packages - defaults to 0
317
genLValueArrayMethods(qw(colors)); # A reference to an array of colours expressed in html format - defaults to B - the background applied to each output section is cycled through these colours to individuate each section.
318
}
319
320
#1 Methods # Methods available
321
322
sub new # Create a new java doc processor
323
1
1
1
15
{bless {}; # Java doc processor
324
}
325
326
sub html($) # Create documentation using html as the output format. Write the generated html to the file specified by L if any and return the generated html as an array of lines.
327
1
1
1
3
{my ($javaDoc) = @_; # Java doc processor
328
329
1
4
$javaDoc->parseJavaFiles; # Parse the input files
330
#say STDERR "AAAA ", dump($javaDoc->parse); exit;
331
1
3
my @h = $javaDoc->htmlJavaFiles; # Write as html
332
333
1
50
44
if (my $file = $javaDoc->target)
334
0
0
{my $h = @h;
335
0
0
writeFile($file, join "\n", @h);
336
0
0
say STDERR "$h lines of documentation written to:\n$file";
337
}
338
@h # Return the generated html
339
1
28
}
340
341
# podDocumentation
342
343
=encoding utf-8
344
345
=head1 Name
346
347
Java::Doc - Extract L
348
from L
349
350
=head1 Synopsis
351
352
use Java::Doc;
353
354
my $j = Java::Doc::new; # New document builder
355
356
$j->source = [qw(~/java/layoutText/LayoutText.java)]; # Source files
357
$j->target = qq(~/java/documentation.html); # Output html
358
$j->indent = 20; # Indentation
359
$j->colors = [map {"#$_"} qw(ccFFFF FFccFF FFFFcc), # Background colours
360
qw(CCccFF FFCCcc ccFFCC)];
361
$j->html; # Create html
362
363
Each source file is parsed for documentation information which is then
364
collated into a colorful cross referenced html file.
365
366
Documentation is extracted for L, L,
367
L.
368
369
=head2 Packages
370
371
Lines matching
372
373
package packageName ;
374
375
are assumed to define packages.
376
377
=head2 Classes
378
379
Lines with comments B/C> are assumed to define classes:
380
381
class className //C
382
383
with the text of the comment being the definition of the class.
384
385
Classes are terminated with:
386
387
} //C className
388
389
which allows class document definitions to be nested.
390
391
=head2 Methods
392
393
Methods are specified by lines with comments matching B/M>:
394
395
methodName () //M
396
397
methodName //M
398
399
with the description of the method contained in the text of the comment
400
extending to the end of the line.
401
402
Constructors should be marked with comments matching B/c> as in:
403
404
methodName //c
405
406
Methods that are overridden should be noted with a comment as in:
407
408
methodName //O=package.class.method
409
410
=head2 Parameters
411
412
Methods that are not overrides and that do have parameters should place the
413
parameters declarations one per line on succeeding lines marked with comments
414
B/P> as in:
415
416
parameterName //P
417
418
=head2 Example
419
420
The following fragment of java code provides an example of documentation held as
421
comments that can be processed by this module:
422
423
package com.appaapps;
424
425
public class Outer //C Layout text on a canvas
426
{public static void draw //M Draw text to fill a fractional area of the canvas
427
(final Canvas canvas) //P Canvas to draw on
428
{}
429
430
class Inner //C Inner class
431
{InnerText() //c Constructor
432
{}
433
} //C Inner
434
} //C Outer
435
436
=head1 Description
437
438
The following sections describe the methods in each functional area of this
439
module. For an alphabetic listing of all methods by name see L.
440
441
442
443
=head1 Attributes
444
445
Attributes that can be set or retrieved by assignment
446
447
=head2 source :lvalue
448
449
A reference to an array of Java source files that contain documentation as well as java
450
451
452
=head2 target :lvalue
453
454
Name of the file to contain the generated documentation
455
456
457
=head2 wellKnownClasses :lvalue
458
459
A reference to an array of urls that contain the class name of well known Java classes such as: L which will be used in place of the class name to make it possible to locate definitions of these other classes.
460
461
462
=head2 indent :lvalue
463
464
Indentation for methods vs classes and classes vs packages - defaults to 0
465
466
467
=head2 colors :lvalue
468
469
A reference to an array of colours expressed in html format - defaults to B - the background applied to each output section is cycled through these colours to individuate each section.
470
471
472
=head1 Methods
473
474
Methods available
475
476
=head2 new()
477
478
Create a new java doc processor
479
480
481
=head2 html($)
482
483
Create documentation using html as the output format. Write the generated html to the file specified by L if any and return the generated html as an array of lines.
484
485
1 $javaDoc Java doc processor
486
487
488
=head1 Index
489
490
491
1 L
492
493
2 L
494
495
3 L
496
497
4 L
498
499
5 L
500
501
6 L
502
503
7 L
504
505
=head1 Installation
506
507
This module is written in 100% Pure Perl and, thus, it is easy to read, use,
508
modify and install.
509
510
Standard L process for building and installing modules:
511
512
perl Build.PL
513
./Build
514
./Build test
515
./Build install
516
517
=head1 Author
518
519
L
520
521
L
522
523
=head1 Copyright
524
525
Copyright (c) 2016-2017 Philip R Brenan.
526
527
This module is free software. It may be used, redistributed and/or modified
528
under the same terms as Perl itself.
529
530
=cut
531
532
533
534
# Tests and documentation
535
536
sub test
537
1
1
0
20
{my $p = __PACKAGE__;
538
1
14
binmode($_, ":utf8") for *STDOUT, *STDERR;
539
1
50
71
return if eval "eof(${p}::DATA)";
540
1
64
my $s = eval "join('', <${p}::DATA>)";
541
1
50
8
$@ and die $@;
542
1
1
666
eval $s;
1
56607
1
9
1
101
543
1
50
363
$@ and die $@;
544
}
545
546
test unless caller;
547
548
1;
549
# podDocumentation
550
__DATA__