File Coverage

blib/lib/Java/Doc.pm
Criterion Covered Total %
statement 167 197 84.7
branch 32 74 43.2
condition 9 25 36.0
subroutine 17 17 100.0
pod 2 9 22.2
total 227 322 70.5


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             );
222             END
223 1         8 for my $package(sort keys %$parse)
224 1         7 {push @h, qq(
$package
225             }
226 1         6 push @h, <
227            
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            
ClassDescription
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            
ReturnsMethodSignatureAttributesLineDescription
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            
NameTypeLineDescription
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 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:
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 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 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__