File Coverage

blib/lib/Java/Doc.pm
Criterion Covered Total %
statement 158 176 89.7
branch 30 54 55.5
condition 8 20 40.0
subroutine 16 16 100.0
pod 2 9 22.2
total 214 275 77.8


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             );
176             END
177 1         4 for my $package(sort keys %$parse)
178 1         3 {push @h, qq(
$package
179             }
180 1         3 push @h, <
181            
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            
ClassDescription
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            
ReturnsMethodSignatureAttributesDescription
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            
NameTypeAttributesDescription
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 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:
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 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 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__