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