File Coverage

blib/lib/Java/Doc.pm
Criterion Covered Total %
statement 161 188 85.6
branch 30 60 50.0
condition 8 20 40.0
subroutine 16 16 100.0
pod 2 9 22.2
total 217 293 74.0


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   478 use warnings FATAL => qw(all);
  1         7  
  1         32  
13 1     1   5 use strict;
  1         1  
  1         19  
14 1     1   4 use Carp qw(confess);
  1         1  
  1         89  
15 1     1   301 use Data::Dump qw(dump);
  1         6138  
  1         71  
16 1     1   560 use Data::Table::Text qw(:all);
  1         44754  
  1         2512  
17              
18             our $VERSION = '20171012';
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 22 {my ($javaDoc, $type) = @_; # Java doc builder, type to replace with an informative url
27 12         44 for my $url(keys %veryWellKnownClassesHash, @{$javaDoc->wellKnownClasses})
  12         212  
28 237         370 {my $i = index($url, "/$type.html");
29 237 100       346 if ($i >= 0)
30             {#say STDERR "AAAA $i $type $url", ;
31 6         44 return qq($type);
32             }
33             }
34             $type
35 6         31 }
36              
37             sub getAttributes($) # Get the attributes from a method type: public, private, final, static etc
38 12     12 0 20 {my ($type) = @_; # Type as parsed out
39 12         26 my $t = qq( $type ); # Blank pad
40 12         30 $t =~ s(\A\s*[\(\{]) ( )gs; # Replace leading open bracket with space
41 12         13 my @attributes; # Method attributes
42 12         20 for(qw(public private protected abstract final static))
43 72 100       488 {push @attributes, $_ if $t =~ m($_)s;
44 72         438 $t =~ s($_) ( )gs;
45             }
46 12         48 $t =~ s(\s+) ( )gs; # Remove excess white space
47 12         34 (trim($t), @attributes)
48             }
49             #say STDERR dump([getAttributes(" (final public static float ")]); exit;
50              
51             sub parseJavaFile($$) # Parse java file for package, class, method, parameters
52 1     1 0 2 {my ($javaDoc, $fileOrString) = @_; # Java doc builder, Parse tree, java file or string of java to process
53 1         16 my $parse = $javaDoc->parse; # Parse tree
54              
55 1 50       8 my $s = $fileOrString =~ m(\n)s ? $fileOrString : readFile($fileOrString);
56              
57 1         9 my $package; # Package we are in
58             my @class; # Containing classes as array
59 1         0 my $class; # Containing classes as a.b.c
60 1         0 my $method; # Method we are in
61 1         12 my $state = 0; # 0 - outer, 1 - parameters to last method
62              
63 1         2 my $line = 0; # Line numbers
64 1         10 for(split /\n/, $s)
65 30         69 {++$line;
66 30 100       53 if ($state == 0)
    50          
67 18 100       137 {if (m(\A\s*package\s+((\w+|\.)+))) # 'package' package ;
    100          
    50          
    100          
68             {#say STDERR "Package = $1";
69 1         3 $package = $1;
70             }
71             elsif (m(\A.*?class\s+(\w+)\s*\{?\s*//C\s+(.*?)\s*\Z)) # Class with optional '{' //C
72 2         7 {push @class, $1; # Save containing class
73 2         9 $class = join '.', @class;
74             #say STDERR "Class $class = $1 = $2";
75 2         47 $javaDoc->classes->{$class} = $parse->{$package}{$class}{comment} = $2; # Record the last encountered class as the class to link to - could be improved!
76             }
77             elsif (m(\A\s*}\s*//C\s+(\w+))) # '}' '//C' className - close class className
78             {#say STDERR "Class = $1 End";
79 0 0       0 if (!@class)
80 0         0 {warn "Closing class $1 but no class to close";
81             }
82 0 0       0 if ($1 ne $class[-1])
83 0         0 {warn "Closing class $1 but in class $class ignored";
84             }
85             else
86             {$parse->{$package}{$class}{methods} =
87             [sort
88 0 0       0 {my $r = $b->{res} cmp $a->{res}; return $r if $r;
  0         0  
89 0 0       0 my $n = $a->{name} cmp $b->{name}; return $n if $n;
  0         0  
90 0         0 my $c = $a->{comment} cmp $b->{comment}; return $c;
  0         0  
91             }
92 0         0 @{$parse->{$package}{$class}{methods}}];
  0         0  
93              
94 0         0 pop @class;
95 0         0 $class = join '.', @class;
96             }
97             }
98             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
99             (m(\A\s*(.*?)
100             \s+(\w+)
101             \s*(\x28\s*\x29)?
102             \s*\x28?\s*
103             //(M|c|O=\S+)\s+(.*?)\s*\Z)x) # Comment
104 3         16 {my ($empty, $res, $comment) = ($3, $4, $5);
105 3         9 $method = $2;
106              
107 3         7 my ($type, @attributes) = getAttributes($1); # Method attributes
108              
109 3         26 my $override; # Method is an override
110 3 50       10 if ($res =~ m(\Ac\Z)s) # In summa it is a constructor
    50          
111 0         0 {push @attributes, q(constructor); # Constructor
112 0         0 $type = qq($method); # Type for a constructor is the constructor name
113             }
114             elsif ($res =~ m(\AO=(.+?)\Z)s) # Override
115 0         0 {$override = $1;
116 0 0       0 my $dot = $comment =~ m(\.\Z)s ? '' : '.';
117 0         0 $comment = qq($comment$dot Overrides: $override);
118             }
119              
120 3 50 33     14 if ($package and $class) # Save method details is possible
121             {#say STDERR "Method = $method == $type == $comment ";
122              
123 3         4 push @{$parse->{$package}{$class}{methods}},
  3         14  
124             {type=>$javaDoc->urlIfKnown($type), name=>$method, res=>$res,
125             comment=>$comment, attributes=>[@attributes], line=>$line};
126 3 50 33     12 $state = 1 if !$empty and !$override; # Get parameters next if method has parameters and is not an override
127             }
128             else
129 0         0 {my $m = qq(Ignoring method $method as no preceding);
130 0         0 my $f = qq(in file:\n$fileOrString\nLine:\n$_);
131 0 0       0 warn "$m package $f" unless $package;
132 0 0       0 warn "$m class $f" unless $class;
133             }
134             }
135             }
136             elsif ($state == 1)
137 12 100       85 {if (m(\A.\s*(.+?)\s+(\w+)\s*[,\)\{]*\s*//P\s+(.*?)\s*\Z)) # type name, optional ',){', //P
138             {#say STDERR "Parameter =$1=$2=$3";
139 9         28 my ($type, $parameter, $comment) = ($1, $2, $3);
140 9         17 my ($t, @attributes) = getAttributes($type);
141 9         71 push @{$parse->{$package}{$class}{methods}[-1]{parameters}},
  9         32  
142             [$javaDoc->urlIfKnown($t), $parameter, $comment, [@attributes],
143             $line];
144             }
145             else # End of parameters if the line does not match
146 3         4 {$state = 0;
147 3 50 33     17 if ($package and $class and $method)
      33        
148 3         8 {my $m = $parse->{$package}{$class}{methods}[-1];
149 3 50       7 if (my $p = $m->{parameters})
150 3 50       8 {if (my @p = @$p)
151 3         5 {$m->{nameSig} = join ', ', map {$_->[1]} @p;
  9         26  
152 3         5 $m->{typeSig} = join ', ', map {$_->[0]} @p;
  9         25  
153             }
154             }
155             }
156             else
157 0         0 {warn "Ignoring method $method as no preceding package or class or ".
158             "method in file:\n$fileOrString\nLine:\n$_";
159             }
160             }
161             }
162             }
163             #say STDERR "AAAA ", dump($parse); exit;
164             $parse
165 1         6 }
166              
167             sub parseJavaFiles($) # Parse all the input files into one parse tree
168 1     1 0 2 {my ($javaDoc) = @_; # Java doc processor
169 1         1 for(@{$javaDoc->source}) # Extend the parse tree with the parse of each source file
  1         15  
170 1         7 {$javaDoc->parseJavaFile($_);
171             }
172             }
173              
174             sub htmlJavaFiles($) # Create documentation using html for all java files from combined parse tree
175 1     1 0 2 {my ($javaDoc) = @_; # Java doc processor, combined parse tree
176 1         19 my $parse = $javaDoc->parse; # Parse tree
177 1   50     18 my $indent = $javaDoc->indent // 0; # Indentation per level
178 1         10 my @c = @{$javaDoc->colors}; # Back ground colours
  1         16  
179 1 50       8 @c = 'white' unless @c;
180 1         2 my $d; # Current background colour - start
181 1         2 my $D = q(); # Current background colour - end
182             my $swapColours = sub # Swap background colour
183 7     7   9 {my ($margin) = @_;
184 7         9 my $m = $margin * $indent;
185 7         11 push @c, my $c = shift @c;
186 7         16 $d = qq(
);
187 1         10 };
188 1         4 &$swapColours(0); # Swap background colour
189              
190 1         3 my @h = <
191             $d
192            
193            
194            
195            
196            

Packages

197             );
198             END
199 1         5 for my $package(sort keys %$parse)
200 1         4 {push @h, qq(
$package
201             }
202 1         3 push @h, <
203            
204             $D
205             END
206              
207 1         3 for my $package(sort keys %$parse)
208 1         1 {my %package = %{$parse->{$package}};
  1         4  
209 1         3 &$swapColours(1);
210 1         3 push @h, <
211            
212             $d
213            

Package: $package

214            
215            
ClassDescription
216             END
217 1         5 for my $class(sort keys %package)
218 2         5 {my %class = %{$package{$class}};
  2         7  
219 2         7 my $classComment = $class{comment};
220 2         18 push @h, <<"END";
221            
$class
222             $classComment
223            
224             END
225             }
226 1         5 push @h, <
227            
228             $D
229             END
230 1         6 for my $class(sort keys %package)
231 2         2 {my %class = %{$package{$class}};
  2         7  
232 2         3 my $classComment = $class{comment};
233 2         4 &$swapColours(2);
234 2         7 push @h, <
235             $d
236            
237            

Class: $class, package: $package

238            

$classComment

239            
240            
ReturnsMethodSignatureAttributesLineDescription
241             END
242 2         3 for my $method(@{$class{methods}})
  2         4  
243 3         3 {my %method = %{$method};
  3         14  
244 3   50     5 my $attr = join ' ', @{$method{attributes}//[]};
  3         9  
245 3         4 my $type = $method{type};
246 3         5 my $name = $method{name};
247 3         3 my $comment = $method{comment};
248 3         4 my $line = $method{line};
249 3   50     7 my $sig = $method{typeSig} // 'void';
250 3         20 push @h, <
251            
$type
252             $name
253             $sig
254             $attr
255             $line
256             $comment
257            
258             END
259             }
260 2         5 push @h, <
261            
262             $D
263             END
264 2         3 for my $method(@{$class{methods}})
  2         3  
265 3         4 {my %method = %{$method};
  3         10  
266 3         6 my $type = $method{type};
267 3         3 my $name = $method{name};
268 3         4 my $comment = $method{comment};
269 3         20 &$swapColours(3);
270 3         12 push @h, <
271             $d
272            
273            

$name returns $type --- in class $class

274            

$comment

275             END
276              
277 3 50       14 if (my $parameters = $method{parameters})
278 3         6 {my @parameters = @$parameters;
279 3         4 push @h, <
280             );
281            
NameTypeLineDescription
282             END
283 3         6 for my $parameter(@parameters)
284 9         15 {my ($type, $name, $comment, $attributes, $line) = @$parameter;
285 9   50     9 my $attr = join ' ', @{$attributes//[]};
  9         20  
286 9         26 push @h, qq(
$name$type$line$comment
287             }
288 3         6 push @h, <
289            
290             END
291             }
292 3         10 push @h, <
293             $D
294            
295             END
296             }
297             }
298             }
299              
300 1         11 s(L<(.+?)>) ($1)gs for @h;
301              
302             @h
303 1         9 }
304              
305 1     1 0 10 sub veryWellKnownClasses {<<'END'}
306             https://developer.android.com/reference/android/app/Activity.html
307             https://developer.android.com/reference/android/content/Context.html
308             https://developer.android.com/reference/android/graphics/BitmapFactory.html
309             https://developer.android.com/reference/android/graphics/Bitmap.html
310             https://developer.android.com/reference/android/graphics/Canvas.html
311             https://developer.android.com/reference/android/graphics/drawable/BitmapDrawable.html
312             https://developer.android.com/reference/android/graphics/drawable/Drawable.html
313             https://developer.android.com/reference/android/graphics/Matrix.html
314             https://developer.android.com/reference/android/graphics/Paint.html
315             https://developer.android.com/reference/android/graphics/Path.html
316             https://developer.android.com/reference/android/graphics/PorterDuff.Mode.html
317             https://developer.android.com/reference/android/graphics/RectF.html
318             https://developer.android.com/reference/android/media/MediaPlayer.html
319             https://developer.android.com/reference/android/util/DisplayMetrics.html
320             https://developer.android.com/reference/java/io/ByteArrayOutputStream.html
321             https://developer.android.com/reference/java/io/DataInputStream.html
322             https://developer.android.com/reference/java/io/DataOutputStream.html
323             https://developer.android.com/reference/java/io/File.html
324             https://developer.android.com/reference/java/io/FileOutputStream.html
325             https://developer.android.com/reference/java/lang/String.html
326             https://developer.android.com/reference/java/lang/Thread.html
327             https://developer.android.com/reference/java/util/Stack.html
328             https://developer.android.com/reference/java/util/TreeMap.html
329             https://developer.android.com/reference/java/util/TreeSet.html
330             https://developer.android.com/studio/command-line/adb.html
331             END
332              
333             #1 Attributes # Attributes that can be set or retrieved by assignment
334              
335             if (1) { # Parameters that can be set by the caller
336             genLValueArrayMethods(qw(source)); # A reference to an array of Java source files that contain documentation as well as java
337             genLValueScalarMethods(qw(target)); # Name of the file to contain the generated documentation
338             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.
339             genLValueScalarMethods(qw(indent)); # Indentation for methods vs classes and classes vs packages - defaults to 0
340             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.
341             }
342              
343             #1 Methods # Methods available
344              
345             sub new # Create a new java doc processor
346 1     1 1 16 {bless {}; # Java doc processor
347             }
348              
349             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.
350 1     1 1 3 {my ($javaDoc) = @_; # Java doc processor
351              
352 1         4 $javaDoc->parseJavaFiles; # Parse the input files
353             #say STDERR "AAAA ", dump($javaDoc->parse); exit;
354 1         3 my @h = $javaDoc->htmlJavaFiles; # Write as html
355              
356 1 50       26 if (my $file = $javaDoc->target)
357 0         0 {my $h = @h;
358 0         0 writeFile($file, join "\n", @h);
359 0         0 say STDERR "$h lines of documentation written to:\n$file";
360             }
361             @h # Return the generated html
362 1         25 }
363              
364             # podDocumentation
365              
366             =encoding utf-8
367              
368             =head1 Name
369              
370             Java::Doc - Extract L
371             from L
372              
373             =head1 Synopsis
374              
375             use Java::Doc;
376              
377             my $j = Java::Doc::new; # New document builder
378              
379             $j->source = [qw(~/java/layoutText/LayoutText.java)]; # Source files
380             $j->target = qq(~/java/documentation.html); # Output html
381             $j->indent = 20; # Indentation
382             $j->colors = [map {"#$_"} qw(ccFFFF FFccFF FFFFcc), # Background colours
383             qw(CCccFF FFCCcc ccFFCC)];
384             $j->html; # Create html
385              
386             Each source file is parsed for documentation information which is then
387             collated into a colorful cross referenced html file.
388              
389             Documentation is extracted for L, L,
390             L.
391              
392             =head2 Packages
393              
394             Lines matching
395              
396             package packageName ;
397              
398             are assumed to define packages.
399              
400             =head2 Classes
401              
402             Lines with comments B are assumed to define classes:
403              
404             class className //C
405              
406             with the text of the comment being the definition of the class.
407              
408             Classes are terminated with:
409              
410             } //C className
411              
412             which allows class document definitions to be nested.
413              
414             =head2 Methods
415              
416             Methods are specified by lines with comments matching B:
417              
418             methodName () //M
419              
420             methodName //M
421              
422             with the description of the method contained in the text of the comment
423             extending to the end of the line.
424              
425             Constructors should be marked with comments matching B as in:
426              
427             methodName //c
428              
429             Methods that are overridden should be noted with a comment as in:
430              
431             methodName //O=package.class.method
432              
433             =head2 Parameters
434              
435             Methods that are not overrides and that do have parameters should place the
436             parameters declarations one per line on succeeding lines marked with comments
437             B as in:
438              
439             parameterName //P
440              
441             =head2 Example
442              
443             The following fragment of java code provides an example of documentation held as
444             comments that can be processed by this module:
445              
446             package com.appaapps;
447              
448             public class Outer //C Layout text on a canvas
449             {public static void draw //M Draw text to fill a fractional area of the canvas
450             (final Canvas canvas) //P Canvas to draw on
451             {}
452              
453             class Inner //C Inner class
454             {InnerText() //c Constructor
455             {}
456             } //C Inner
457             } //C Outer
458              
459             =head1 Description
460              
461             The following sections describe the methods in each functional area of this
462             module. For an alphabetic listing of all methods by name see L.
463              
464              
465              
466             =head1 Attributes
467              
468             Attributes that can be set or retrieved by assignment
469              
470             =head2 source :lvalue
471              
472             A reference to an array of Java source files that contain documentation as well as java
473              
474              
475             =head2 target :lvalue
476              
477             Name of the file to contain the generated documentation
478              
479              
480             =head2 wellKnownClasses :lvalue
481              
482             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.
483              
484              
485             =head2 indent :lvalue
486              
487             Indentation for methods vs classes and classes vs packages - defaults to 0
488              
489              
490             =head2 colors :lvalue
491              
492             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.
493              
494              
495             =head1 Methods
496              
497             Methods available
498              
499             =head2 new()
500              
501             Create a new java doc processor
502              
503              
504             =head2 html($)
505              
506             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.
507              
508             1 $javaDoc Java doc processor
509              
510              
511             =head1 Index
512              
513              
514             1 L
515              
516             2 L
517              
518             3 L
519              
520             4 L
521              
522             5 L
523              
524             6 L
525              
526             7 L
527              
528             =head1 Installation
529              
530             This module is written in 100% Pure Perl and, thus, it is easy to read, use,
531             modify and install.
532              
533             Standard L process for building and installing modules:
534              
535             perl Build.PL
536             ./Build
537             ./Build test
538             ./Build install
539              
540             =head1 Author
541              
542             L
543              
544             L
545              
546             =head1 Copyright
547              
548             Copyright (c) 2016-2017 Philip R Brenan.
549              
550             This module is free software. It may be used, redistributed and/or modified
551             under the same terms as Perl itself.
552              
553             =cut
554              
555              
556              
557             # Tests and documentation
558              
559             sub test
560 1     1 0 10 {my $p = __PACKAGE__;
561 1         8 binmode($_, ":utf8") for *STDOUT, *STDERR;
562 1 50       64 return if eval "eof(${p}::DATA)";
563 1         41 my $s = eval "join('', <${p}::DATA>)";
564 1 50       8 $@ and die $@;
565 1     1   449 eval $s;
  1         49120  
  1         8  
  1         66  
566 1 50       466 $@ and die $@;
567             }
568              
569             test unless caller;
570              
571             1;
572             # podDocumentation
573             __DATA__