lib/Doxygen/Filter/Perl.pm | |||
---|---|---|---|
Criterion | Covered | Total | % |
statement | 96 | 504 | 19.0 |
branch | 14 | 182 | 7.6 |
condition | 3 | 60 | 5.0 |
subroutine | 20 | 36 | 55.5 |
pod | 0 | 11 | 0.0 |
total | 133 | 793 | 16.7 |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | #** @file Perl.pm | ||||||
2 | # @verbatim | ||||||
3 | ##################################################################### | ||||||
4 | # This program is not guaranteed to work at all, and by using this # | ||||||
5 | # program you release the author of any and all liability. # | ||||||
6 | # # | ||||||
7 | # You may use this code as long as you are in compliance with the # | ||||||
8 | # license (see the LICENSE file) and this notice, disclaimer and # | ||||||
9 | # comment box remain intact and unchanged. # | ||||||
10 | # # | ||||||
11 | # Package: Doxygen::Filter # | ||||||
12 | # Class: Perl # | ||||||
13 | # Description: Methods for prefiltering Perl code for Doxygen # | ||||||
14 | # # | ||||||
15 | # Written by: Bret Jordan (jordan at open1x littledot org) # | ||||||
16 | # Created: 2011-10-13 # | ||||||
17 | ##################################################################### | ||||||
18 | # @endverbatim | ||||||
19 | # | ||||||
20 | # @copy 2011, Bret Jordan (jordan2175@gmail.com, jordan@open1x.org) | ||||||
21 | # $Id: Perl.pm 93 2015-03-17 13:08:02Z jordan2175 $ | ||||||
22 | #* | ||||||
23 | package Doxygen::Filter::Perl; | ||||||
24 | |||||||
25 | 1 | 1 | 1663 | use 5.8.8; | |||
1 | 3 | ||||||
1 | 45 | ||||||
26 | 1 | 1 | 4 | use strict; | |||
1 | 1 | ||||||
1 | 22 | ||||||
27 | 1 | 1 | 3 | use warnings; | |||
1 | 4 | ||||||
1 | 22 | ||||||
28 | 1 | 1 | 398 | use parent qw(Doxygen::Filter); | |||
1 | 265 | ||||||
1 | 3 | ||||||
29 | 1 | 1 | 44 | use Log::Log4perl; | |||
1 | 2 | ||||||
1 | 3 | ||||||
30 | 1 | 1 | 627 | use Pod::POM; | |||
1 | 18934 | ||||||
1 | 46 | ||||||
31 | 1 | 1 | 515 | use IO::Handle; | |||
1 | 4944 | ||||||
1 | 39 | ||||||
32 | 1 | 1 | 297 | use Doxygen::Filter::Perl::POD; | |||
1 | 3 | ||||||
1 | 5725 | ||||||
33 | |||||||
34 | our $VERSION = '1.71'; | ||||||
35 | $VERSION = eval $VERSION; | ||||||
36 | |||||||
37 | |||||||
38 | # Define State Engine Values | ||||||
39 | my $hValidStates = { | ||||||
40 | 'NORMAL' => 0, | ||||||
41 | 'COMMENT' => 1, | ||||||
42 | 'DOXYGEN' => 2, | ||||||
43 | 'POD' => 3, | ||||||
44 | 'METHOD' => 4, | ||||||
45 | 'DOXYFILE' => 21, | ||||||
46 | 'DOXYCLASS' => 22, | ||||||
47 | 'DOXYFUNCTION' => 23, | ||||||
48 | 'DOXYMETHOD' => 24, | ||||||
49 | 'DOXYCOMMENT' => 25, | ||||||
50 | }; | ||||||
51 | |||||||
52 | |||||||
53 | our %SYSTEM_PACKAGES = map({ $_ => 1 } qw( | ||||||
54 | base | ||||||
55 | warnings | ||||||
56 | strict | ||||||
57 | Exporter | ||||||
58 | vars | ||||||
59 | )); | ||||||
60 | |||||||
61 | |||||||
62 | |||||||
63 | sub new | ||||||
64 | { | ||||||
65 | #** @method private new () | ||||||
66 | # This is the constructor and it calls _init() to initiate | ||||||
67 | # the various variables | ||||||
68 | #* | ||||||
69 | 1 | 1 | 0 | 11 | my $pkg = shift; | ||
70 | 1 | 33 | 7 | my $class = ref($pkg) || $pkg; | |||
71 | |||||||
72 | 1 | 2 | my $self = {}; | ||||
73 | 1 | 2 | bless ($self, $class); | ||||
74 | |||||||
75 | # Lets send any passed in arguments to the _init method | ||||||
76 | 1 | 24 | $self->_init(@_); | ||||
77 | 1 | 2 | return $self; | ||||
78 | } | ||||||
79 | |||||||
80 | sub DESTROY | ||||||
81 | { | ||||||
82 | #** @method private DESTROY () | ||||||
83 | # This is the destructor | ||||||
84 | #* | ||||||
85 | 1 | 1 | 1894 | my $self = shift; | |||
86 | 1 | 34 | $self = {}; | ||||
87 | } | ||||||
88 | |||||||
89 | sub RESETSUB | ||||||
90 | { | ||||||
91 | 1 | 1 | 0 | 1 | my $self = shift; | ||
92 | 1 | 2 | $self->{'_iOpenBrace'} = 0; | ||||
93 | 1 | 3 | $self->{'_iCloseBrace'} = 0; | ||||
94 | 1 | 2 | $self->{'_sCurrentMethodName'} = undef; | ||||
95 | 1 | 2 | $self->{'_sCurrentMethodType'} = undef; | ||||
96 | 1 | 2 | $self->{'_sCurrentMethodState'} = undef; | ||||
97 | } | ||||||
98 | |||||||
99 | 1 | 1 | 0 | 3 | sub RESETFILE { shift->{'_aRawFileData'} = []; } | ||
100 | |||||||
101 | sub RESETCLASS | ||||||
102 | { | ||||||
103 | 1 | 1 | 0 | 1 | my $self = shift; | ||
104 | #$self->{'_sCurrentClass'} = 'main'; | ||||||
105 | #push (@{$self->{'_hData'}->{'class'}->{'classorder'}}, 'main'); | ||||||
106 | 1 | 4 | $self->_SwitchClass('main'); | ||||
107 | } | ||||||
108 | |||||||
109 | 1 | 1 | 0 | 2 | sub RESETDOXY { shift->{'_aDoxygenBlock'} = []; } | ||
110 | 1 | 1 | 0 | 2 | sub RESETPOD { shift->{'_aPodBlock'} = []; } | ||
111 | |||||||
112 | |||||||
113 | |||||||
114 | sub _init | ||||||
115 | { | ||||||
116 | #** @method private _init () | ||||||
117 | # This method is used in the constructor to initiate | ||||||
118 | # the various variables in the object | ||||||
119 | #* | ||||||
120 | 1 | 1 | 1 | my $self = shift; | |||
121 | 1 | 8 | $self->{'_iDebug'} = 0; | ||||
122 | 1 | 2 | $self->{'_sState'} = undef; | ||||
123 | 1 | 2 | $self->{'_sPreviousState'} = []; | ||||
124 | 1 | 4 | $self->_ChangeState('NORMAL'); | ||||
125 | 1 | 1 | $self->{'_hData'} = {}; | ||||
126 | 1 | 6 | $self->RESETFILE(); | ||||
127 | 1 | 3 | $self->RESETCLASS(); | ||||
128 | 1 | 4 | $self->RESETSUB(); | ||||
129 | 1 | 3 | $self->RESETDOXY(); | ||||
130 | 1 | 3 | $self->RESETPOD(); | ||||
131 | } | ||||||
132 | |||||||
133 | |||||||
134 | |||||||
135 | |||||||
136 | # ---------------------------------------- | ||||||
137 | # Public Methods | ||||||
138 | # ---------------------------------------- | ||||||
139 | sub GetCurrentClass | ||||||
140 | { | ||||||
141 | 0 | 0 | 0 | 0 | my $self = shift; | ||
142 | 0 | 0 | return $self->{'_hData'}->{'class'}->{$self->{'_sCurrentClass'}}; | ||||
143 | } | ||||||
144 | |||||||
145 | sub ReadFile | ||||||
146 | { | ||||||
147 | #** @method public ReadFile ($sFilename) | ||||||
148 | # This method will read the contents of the file in to an array | ||||||
149 | # and store that in the object as $self->{'_aRawFileData'} | ||||||
150 | # @param sFilename - required string (filename to use) | ||||||
151 | #* | ||||||
152 | 0 | 0 | 0 | 0 | my $self = shift; | ||
153 | 0 | 0 | my $sFilename = shift; | ||||
154 | 0 | 0 | my $logger = $self->GetLogger($self); | ||||
155 | 0 | 0 | $logger->debug("### Entering ReadFile ###"); | ||||
156 | |||||||
157 | # Lets record the file name in the data structure | ||||||
158 | 0 | 0 | $self->{'_hData'}->{'filename'}->{'fullpath'} = $sFilename; | ||||
159 | |||||||
160 | # Replace forward slash with a black slash | ||||||
161 | 0 | 0 | $sFilename =~ s/\\/\//g; | ||||
162 | # Remove windows style drive letters | ||||||
163 | 0 | 0 | $sFilename =~ s/^.*://; | ||||
164 | |||||||
165 | # Lets grab just the file name not the full path for the short name | ||||||
166 | 0 | 0 | $sFilename =~ /^(.*\/)*(.*)$/; | ||||
167 | 0 | 0 | $self->{'_hData'}->{'filename'}->{'shortname'} = $2; | ||||
168 | |||||||
169 | 0 | 0 | open(DATAIN, $sFilename); | ||||
170 | #my @aFileData = |
||||||
171 | 0 | 0 | my @aFileData = map({ s/\r$//g; $_; } |
||||
0 | 0 | ||||||
0 | 0 | ||||||
172 | 0 | 0 | close (DATAIN); | ||||
173 | 0 | 0 | $self->{'_aRawFileData'} = \@aFileData; | ||||
174 | } | ||||||
175 | |||||||
176 | sub ReportError | ||||||
177 | { | ||||||
178 | #** @method public void ReportError($message) | ||||||
179 | # @brief Reports an error message in the current context. | ||||||
180 | # | ||||||
181 | # The message is prepended by 'filename:lineno: error:' prefix so it is easily | ||||||
182 | # parseable by IDEs and advanced editors. | ||||||
183 | #* | ||||||
184 | 0 | 0 | 0 | 0 | my $self = shift; | ||
185 | 0 | 0 | my $message = shift; | ||||
186 | |||||||
187 | 0 | 0 | my $hData = $self->{'_hData'}; | ||||
188 | 0 | 0 | my $header = "$hData->{filename}->{fullpath}:$hData->{lineno}: error: "; | ||||
189 | 0 | 0 | 0 | $message .= "\n" if (substr($message, -1, 1) ne "\n"); | |||
190 | 0 | 0 | $message =~ s/^/$header/gm; | ||||
191 | 0 | 0 | STDERR->print($message); | ||||
192 | } | ||||||
193 | |||||||
194 | sub ProcessFile | ||||||
195 | { | ||||||
196 | #** @method public ProcessFile () | ||||||
197 | # This method is a state machine that will search down each line of code to see what it should do | ||||||
198 | #* | ||||||
199 | 3 | 3 | 0 | 715 | my $self = shift; | ||
200 | 3 | 8 | my $logger = $self->GetLogger($self); | ||||
201 | 3 | 265 | $logger->debug("### Entering ProcessFile ###"); | ||||
202 | |||||||
203 | 3 | 17 | $self->{'_hData'}->{'lineno'} = 0; | ||||
204 | 3 | 2 | foreach my $line (@{$self->{'_aRawFileData'}}) | ||||
3 | 8 | ||||||
205 | { | ||||||
206 | 7 | 9 | $self->{'_hData'}->{'lineno'}++; | ||||
207 | # Convert syntax block header to supported doxygen form, if this line is a header | ||||||
208 | 7 | 13 | $line = $self->_ConvertToOfficialDoxygenSyntax($line); | ||||
209 | |||||||
210 | # Lets first figure out what state we SHOULD be in and then we will deal with | ||||||
211 | # processing that state. This first block should walk through all the possible | ||||||
212 | # transition states, aka, the states you can get to from the state you are in. | ||||||
213 | 7 | 50 | 11 | if ($self->{'_sState'} eq 'NORMAL') | |||
0 | |||||||
0 | |||||||
0 | |||||||
214 | { | ||||||
215 | 7 | 15 | $logger->debug("We are in state: NORMAL"); | ||||
216 | 7 | 50 | 45 | if ($line =~ /^\s*sub\s*(.*)/) { $self->_ChangeState('METHOD'); } | |||
0 | 50 | 0 | |||||
50 | |||||||
217 | 0 | 0 | elsif ($line =~ /^\s*#\*\*\s*\@/) { $self->_ChangeState('DOXYGEN'); } | ||||
218 | 0 | 0 | elsif ($line =~ /^=.*/) { $self->_ChangeState('POD'); } | ||||
219 | } | ||||||
220 | elsif ($self->{'_sState'} eq 'METHOD') | ||||||
221 | { | ||||||
222 | 0 | 0 | $logger->debug("We are in state: METHOD"); | ||||
223 | 0 | 0 | 0 | if ($line =~ /^\s*#\*\*\s*\@/ ) { $self->_ChangeState('DOXYGEN'); } | |||
0 | 0 | ||||||
224 | } | ||||||
225 | elsif ($self->{'_sState'} eq 'DOXYGEN') | ||||||
226 | { | ||||||
227 | 0 | 0 | $logger->debug("We are in state: DOXYGEN"); | ||||
228 | # If there are no more comments, then reset the state to the previous state | ||||||
229 | 0 | 0 | 0 | unless ($line =~ /^\s*#/) | |||
230 | { | ||||||
231 | # The general idea is we gather the whole doxygen comment in to an array and process | ||||||
232 | # that array all at once in the _ProcessDoxygenCommentBlock. This way we do not have | ||||||
233 | # to artifically keep track of what type of comment block it is between each line | ||||||
234 | # that we read from the file. | ||||||
235 | 0 | 0 | $logger->debug("End of Doxygen Comment Block"); | ||||
236 | 0 | 0 | $self->_ProcessDoxygenCommentBlock(); | ||||
237 | 0 | 0 | $self->_RestoreState(); | ||||
238 | 0 | 0 | $logger->debug("We are in state $self->{'_sState'}"); | ||||
239 | 0 | 0 | 0 | if ($self->{'_sState'} eq 'NORMAL') | |||
240 | { | ||||||
241 | # If this comment block is right next to a subroutine, lets make sure we | ||||||
242 | # handle that condition | ||||||
243 | 0 | 0 | 0 | if ($line =~ /^\s*sub\s*(.*)/) { $self->_ChangeState('METHOD'); } | |||
0 | 0 | ||||||
244 | } | ||||||
245 | } | ||||||
246 | } | ||||||
247 | elsif ($self->{'_sState'} eq 'POD') | ||||||
248 | { | ||||||
249 | 0 | 0 | 0 | if ($line =~ /^=cut/) | |||
250 | { | ||||||
251 | 0 | 0 | push (@{$self->{'_aPodBlock'}}, $line); | ||||
0 | 0 | ||||||
252 | 0 | 0 | $self->_ProcessPodCommentBlock(); | ||||
253 | 0 | 0 | $self->_RestoreState(); | ||||
254 | } | ||||||
255 | } | ||||||
256 | |||||||
257 | |||||||
258 | # Process states | ||||||
259 | 7 | 50 | 13 | if ($self->{'_sState'} eq 'NORMAL') | |||
0 | |||||||
0 | |||||||
0 | |||||||
260 | { | ||||||
261 | 7 | 50 | 51 | if ($line =~ /^\s*package\s*(.*)\;$/) | |||
100 | |||||||
50 | |||||||
50 | |||||||
262 | { | ||||||
263 | #$self->{'_sCurrentClass'} = $1; | ||||||
264 | #push (@{$self->{'_hData'}->{'class'}->{'classorder'}}, $1); | ||||||
265 | 0 | 0 | $self->_SwitchClass($1); | ||||
266 | } | ||||||
267 | elsif ($line =~ /our\s+\$VERSION\s*=\s*(.*);$/) | ||||||
268 | { | ||||||
269 | # our $VERSION = '0.99_01'; | ||||||
270 | # use version; our $VERSION = qv('0.3.1'); - Thanks Hoppfrosch for the suggestion | ||||||
271 | 2 | 6 | my $version = $1; | ||||
272 | 2 | 16 | $version =~ s/[\'\"\(\)\;]//g; | ||||
273 | 2 | 4 | $version =~ s/qv//; | ||||
274 | 2 | 6 | $self->{'_hData'}->{'filename'}->{'version'} = $version; | ||||
275 | } | ||||||
276 | #elsif ($line =~ /^\s*use\s+([\w:]+)/) | ||||||
277 | elsif ($line =~ /^\s*use\s+([\w:]+)(|\s*(\S.*?)\s*;*)$/) | ||||||
278 | { | ||||||
279 | 0 | 0 | my $sIncludeModule = $1; | ||||
280 | 0 | 0 | my $x = $2; | ||||
281 | 0 | 0 | my $expr = $3; | ||||
282 | 0 | 0 | 0 | if (defined($sIncludeModule)) | |||
283 | { | ||||||
284 | #unless ($sIncludeModule eq "strict" || $sIncludeModule eq "warnings" || $sIncludeModule eq "vars" || $sIncludeModule eq "Exporter" || $sIncludeModule eq "base") | ||||||
285 | 0 | 0 | 0 | if ($sIncludeModule =~ m/^(base|strict|warnings|vars|Exporter)$/) | |||
286 | { | ||||||
287 | 0 | 0 | 0 | if ($sIncludeModule eq "base") | |||
288 | { | ||||||
289 | 0 | 0 | my @isa = eval($expr); | ||||
290 | 0 | 0 | 0 | push(@{$self->GetCurrentClass()->{inherits}}, _FilterOutSystemPackages(@isa)) unless ($@); | |||
0 | 0 | ||||||
291 | } | ||||||
292 | else | ||||||
293 | { | ||||||
294 | # ignore other system modules | ||||||
295 | } | ||||||
296 | } | ||||||
297 | else | ||||||
298 | { | ||||||
299 | # Allows doxygen to know where to look for other packages | ||||||
300 | 0 | 0 | $sIncludeModule =~ s/::/\//g; | ||||
301 | 0 | 0 | push (@{$self->{'_hData'}->{'includes'}}, $sIncludeModule); | ||||
0 | 0 | ||||||
302 | } | ||||||
303 | } | ||||||
304 | } | ||||||
305 | #elsif ($line =~ /^\s*(?:Readonly\s+)?(?:my|our)\s+([\$@%*]\w+)/) | ||||||
306 | #elsif ($line =~ /^\s*(?:Readonly\s+)?(my|our)\s+([\$@%*]\w+)([^=]*|\s*=\s*(\S.*?)\s*;*)$/) | ||||||
307 | elsif ($line =~ /^\s*(?:Readonly\s+)?(my|our)\s+(([\$@%*])(\w+))([^=]*|\s*=\s*(\S.*?)\s*;*)$/) | ||||||
308 | { | ||||||
309 | # Lets look for locally defined variables/arrays/hashes and capture them such as: | ||||||
310 | # my $var; | ||||||
311 | # my $var = ... | ||||||
312 | # our @var = ... | ||||||
313 | # Readonly our %var ... | ||||||
314 | #my $sAttrName = $1; | ||||||
315 | #if (defined($sAttrName) && $sAttrName !~ m/^(\@EXPORT|\@EXPORT_OK|\$VERSION)$/) | ||||||
316 | 0 | 0 | my $scope = $1; | ||||
317 | 0 | 0 | my $fullName = $2; | ||||
318 | 0 | 0 | my $typeCode = $3; | ||||
319 | 0 | 0 | my $sAttrName = $4; | ||||
320 | 0 | 0 | my $expr = $6; | ||||
321 | |||||||
322 | 0 | 0 | 0 | if (defined $sAttrName) | |||
323 | { | ||||||
324 | #my $sClassName = $self->{'_sCurrentClass'}; | ||||||
325 | #push (@{$self->{'_hData'}->{'class'}->{$sClassName}->{attributeorder}}, $sAttrName); | ||||||
326 | 0 | 0 | 0 | 0 | if ($scope eq "our" && $fullName =~ m/^(\@ISA|\@EXPORT|\@EXPORT_OK|\$VERSION)$/) | ||
327 | { | ||||||
328 | 0 | 0 | 0 | 0 | if ($fullName eq "\@ISA" && defined $expr) | ||
329 | { | ||||||
330 | 0 | 0 | my @isa = eval($expr); | ||||
331 | 0 | 0 | 0 | push(@{$self->GetCurrentClass()->{inherits}}, _FilterOutSystemPackages(@isa)) unless ($@); | |||
0 | 0 | ||||||
332 | } | ||||||
333 | else | ||||||
334 | { | ||||||
335 | # ignore other system variables | ||||||
336 | } | ||||||
337 | } | ||||||
338 | else | ||||||
339 | { | ||||||
340 | 0 | 0 | my $sClassName = $self->{'_sCurrentClass'}; | ||||
341 | 0 | 0 | 0 | if (!exists $self->{'_hData'}->{'class'}->{$sClassName}->{attributes}->{$sAttrName}) | |||
342 | { | ||||||
343 | # only define the attribute if it was not yet defined by doxygen comment | ||||||
344 | 0 | 0 | 0 | my $attrDef = $self->{'_hData'}->{'class'}->{$sClassName}->{attributes}->{$sAttrName} = { | |||
345 | type => $self->_ConvertTypeCode($typeCode), | ||||||
346 | modifiers => "static ", | ||||||
347 | state => $scope eq "my" ? "private" : "public", | ||||||
348 | }; | ||||||
349 | 0 | 0 | push(@{$self->{'_hData'}->{'class'}->{$sClassName}->{attributeorder}}, $sAttrName); | ||||
0 | 0 | ||||||
350 | } | ||||||
351 | } | ||||||
352 | } | ||||||
353 | 0 | 0 | 0 | if ($line =~ /(#\*\*\s+\@.*$)/) | |||
354 | { | ||||||
355 | # Lets look for an single in-line doxygen comment on a variable, array, or hash declaration | ||||||
356 | 0 | 0 | my $sBlock = $1; | ||||
357 | 0 | 0 | push (@{$self->{'_aDoxygenBlock'}}, $sBlock); | ||||
0 | 0 | ||||||
358 | 0 | 0 | $self->_ProcessDoxygenCommentBlock(); | ||||
359 | } | ||||||
360 | } | ||||||
361 | } | ||||||
362 | 0 | 0 | elsif ($self->{'_sState'} eq 'METHOD') { $self->_ProcessPerlMethod($line); } | ||||
363 | 0 | 0 | elsif ($self->{'_sState'} eq 'DOXYGEN') { push (@{$self->{'_aDoxygenBlock'}}, $line); } | ||||
0 | 0 | ||||||
364 | 0 | 0 | elsif ($self->{'_sState'} eq 'POD') { push (@{$self->{'_aPodBlock'}}, $line);} | ||||
0 | 0 | ||||||
365 | } | ||||||
366 | } | ||||||
367 | |||||||
368 | sub PrintAll | ||||||
369 | { | ||||||
370 | #** @method public PrintAll () | ||||||
371 | # This method will print out the entire data structure in a form that Doxygen can work with. | ||||||
372 | # It is important to note that you are basically making the output look like C code so that | ||||||
373 | # packages and classes need to have start and end blocks and need to include all of the | ||||||
374 | # elements that are part of that package or class | ||||||
375 | #* | ||||||
376 | 0 | 0 | 0 | 0 | my $self = shift; | ||
377 | 0 | 0 | my $logger = $self->GetLogger($self); | ||||
378 | 0 | 0 | $logger->debug("### Entering PrintAll ###"); | ||||
379 | |||||||
380 | 0 | 0 | $self->_PrintFilenameBlock(); | ||||
381 | 0 | 0 | $self->_PrintIncludesBlock(); | ||||
382 | |||||||
383 | 0 | 0 | foreach my $class (@{$self->{'_hData'}->{'class'}->{'classorder'}}) | ||||
0 | 0 | ||||||
384 | { | ||||||
385 | 0 | 0 | my $classDef = $self->{'_hData'}->{'class'}->{$class}; | ||||
386 | |||||||
387 | # skip the default main class unless we really have something to print | ||||||
388 | 0 | 0 | 0 | 0 | if ($class eq "main" && | ||
0 | 0 | 0 | |||||
0 | |||||||
0 | |||||||
389 | 0 | 0 | @{$classDef->{attributeorder}} == 0 && | ||||
390 | @{$classDef->{subroutineorder}} == 0 && | ||||||
391 | (!defined $classDef->{details}) && | ||||||
392 | (!defined $classDef->{comments}) | ||||||
393 | ) | ||||||
394 | { | ||||||
395 | 0 | 0 | next; | ||||
396 | } | ||||||
397 | |||||||
398 | 0 | 0 | $self->_PrintClassBlock($class); | ||||
399 | |||||||
400 | # Print all available attributes first that are defined at the global class level | ||||||
401 | 0 | 0 | foreach my $sAttrName (@{$self->{'_hData'}->{'class'}->{$class}->{'attributeorder'}}) | ||||
0 | 0 | ||||||
402 | { | ||||||
403 | 0 | 0 | my $attrDef = $self->{'_hData'}->{'class'}->{$class}->{'attributes'}->{$sAttrName}; | ||||
404 | |||||||
405 | 0 | 0 | 0 | my $sState = $attrDef->{'state'} || 'public'; | |||
406 | 0 | 0 | my $sComments = $attrDef->{'comments'}; | ||||
407 | 0 | 0 | my $sDetails = $attrDef->{'details'}; | ||||
408 | 0 | 0 | 0 | 0 | if (defined $sComments || defined $sDetails) | ||
409 | { | ||||||
410 | 0 | 0 | print "/**\n"; | ||||
411 | 0 | 0 | 0 | if (defined $sComments) | |||
412 | { | ||||||
413 | 0 | 0 | print " \* \@brief $sComments\n"; | ||||
414 | } | ||||||
415 | |||||||
416 | 0 | 0 | 0 | if ($sDetails) | |||
417 | { | ||||||
418 | 0 | 0 | print " * \n".$sDetails; | ||||
419 | } | ||||||
420 | |||||||
421 | 0 | 0 | print " */\n"; | ||||
422 | } | ||||||
423 | |||||||
424 | 0 | 0 | print("$sState:\n$attrDef->{modifiers}$attrDef->{type} $sAttrName;\n\n"); | ||||
425 | } | ||||||
426 | |||||||
427 | # Print all functions/methods in order of appearance, let doxygen take care of grouping them according to modifiers | ||||||
428 | # I added this print public line to make sure the functions print if one of | ||||||
429 | # the previous elements was a my $a = 1 and thus had a print "private:" | ||||||
430 | 0 | 0 | print("public:\n"); | ||||
431 | 0 | 0 | foreach my $methodName (@{$self->{'_hData'}->{'class'}->{$class}->{'subroutineorder'}}) | ||||
0 | 0 | ||||||
432 | { | ||||||
433 | 0 | 0 | $self->_PrintMethodBlock($class, $methodName); | ||||
434 | } | ||||||
435 | # Print end of class mark | ||||||
436 | 0 | 0 | print "}\;\n"; | ||||
437 | # print end of namespace if class is nested | ||||||
438 | 0 | 0 | 0 | print "};\n" if ($class =~ /::/); | |||
439 | } | ||||||
440 | } | ||||||
441 | |||||||
442 | |||||||
443 | # ---------------------------------------- | ||||||
444 | # Private Methods | ||||||
445 | # ---------------------------------------- | ||||||
446 | 0 | 0 | 0 | sub _FilterOutSystemPackages { return grep({ !exists $SYSTEM_PACKAGES{$_} } @_); } | |||
0 | 0 | ||||||
447 | |||||||
448 | sub _SwitchClass | ||||||
449 | { | ||||||
450 | 1 | 1 | 2 | my $self = shift; | |||
451 | 1 | 5 | my $class = shift; | ||||
452 | |||||||
453 | 1 | 3 | $self->{'_sCurrentClass'} = $class; | ||||
454 | 1 | 50 | 5 | if (!exists $self->{'_hData'}->{'class'}->{$class}) | |||
455 | { | ||||||
456 | 1 | 1 | push(@{$self->{'_hData'}->{'class'}->{'classorder'}}, $class); | ||||
1 | 3 | ||||||
457 | 1 | 8 | $self->{'_hData'}->{'class'}->{$class} = { | ||||
458 | classname => $class, | ||||||
459 | inherits => [], | ||||||
460 | attributeorder => [], | ||||||
461 | subroutineorder => [], | ||||||
462 | }; | ||||||
463 | } | ||||||
464 | |||||||
465 | 1 | 2 | return $self->{'_hData'}->{'class'}->{$class}; | ||||
466 | } | ||||||
467 | |||||||
468 | 0 | 0 | 0 | sub _RestoreState { shift->_ChangeState(); } | |||
469 | sub _ChangeState | ||||||
470 | { | ||||||
471 | #** @method private _ChangeState ($state) | ||||||
472 | # This method will change and keep track of the various states that the state machine | ||||||
473 | # transitions to and from. Having this information allows you to return to a previous | ||||||
474 | # state. If you pass nothing in to this method it will restore the previous state. | ||||||
475 | # @param state - optional string (state to change to) | ||||||
476 | #* | ||||||
477 | 1 | 1 | 1 | my $self = shift; | |||
478 | 1 | 2 | my $state = shift; | ||||
479 | 1 | 7 | my $logger = $self->GetLogger($self); | ||||
480 | 1 | 325 | $logger->debug("### Entering _ChangeState ###"); | ||||
481 | |||||||
482 | 1 | 50 | 33 | 61 | if (defined $state && exists $hValidStates->{$state}) | ||
483 | { | ||||||
484 | # If there was a value passed in and it is a valid value lets make it active | ||||||
485 | 1 | 5 | $logger->debug("State passed in: $state"); | ||||
486 | 1 | 50 | 33 | 8 | unless (defined $self->{'_sState'} && $self->{'_sState'} eq $state) | ||
487 | { | ||||||
488 | # Need to push the current state to the array BEFORE we change it and only | ||||||
489 | # if we are not currently at that state | ||||||
490 | 1 | 2 | push (@{$self->{'_sPreviousState'}}, $self->{'_sState'}); | ||||
1 | 2 | ||||||
491 | 1 | 3 | $self->{'_sState'} = $state; | ||||
492 | } | ||||||
493 | } | ||||||
494 | else | ||||||
495 | { | ||||||
496 | # If nothing is passed in, lets set the current state to the preivous state. | ||||||
497 | 0 | 0 | $logger->debug("No state passed in, lets revert to previous state"); | ||||
498 | 0 | 0 | my $previous = pop @{$self->{'_sPreviousState'}}; | ||||
0 | 0 | ||||||
499 | 0 | 0 | 0 | if (defined $previous) | |||
500 | { | ||||||
501 | 0 | 0 | $logger->debug("Previous state was $previous"); | ||||
502 | } | ||||||
503 | else | ||||||
504 | { | ||||||
505 | 0 | 0 | $logger->error("There is no previous state! Setting to NORMAL"); | ||||
506 | 0 | 0 | $previous = 'NORMAL'; | ||||
507 | } | ||||||
508 | 0 | 0 | $self->{'_sState'} = $previous; | ||||
509 | } | ||||||
510 | } | ||||||
511 | |||||||
512 | sub _PrintFilenameBlock | ||||||
513 | { | ||||||
514 | #** @method private _PrintFilenameBlock () | ||||||
515 | # This method will print the filename section in appropriate doxygen syntax | ||||||
516 | #* | ||||||
517 | 0 | 0 | 0 | my $self = shift; | |||
518 | 0 | 0 | my $logger = $self->GetLogger($self); | ||||
519 | 0 | 0 | $logger->debug("### Entering _PrintFilenameBlock ###"); | ||||
520 | |||||||
521 | 0 | 0 | 0 | if (defined $self->{'_hData'}->{'filename'}->{'fullpath'}) | |||
522 | { | ||||||
523 | 0 | 0 | print "/** \@file $self->{'_hData'}->{'filename'}->{'fullpath'}\n"; | ||||
524 | 0 | 0 | 0 | if (defined $self->{'_hData'}->{'filename'}->{'details'}) { print "$self->{'_hData'}->{'filename'}->{'details'}\n"; } | |||
0 | 0 | ||||||
525 | 0 | 0 | 0 | if (defined $self->{'_hData'}->{'filename'}->{'version'}) { print "\@version $self->{'_hData'}->{'filename'}->{'version'}\n"; } | |||
0 | 0 | ||||||
526 | 0 | 0 | print "*/\n"; | ||||
527 | } | ||||||
528 | } | ||||||
529 | |||||||
530 | sub _PrintIncludesBlock | ||||||
531 | { | ||||||
532 | #** @method private _PrintIncludesBlock () | ||||||
533 | # This method will print the various extra modules that are used | ||||||
534 | #* | ||||||
535 | 0 | 0 | 0 | my $self = shift; | |||
536 | 0 | 0 | my $logger = $self->GetLogger($self); | ||||
537 | 0 | 0 | $logger->debug("### Entering _PrintIncludeBlock ###"); | ||||
538 | |||||||
539 | 0 | 0 | foreach my $include (@{$self->{'_hData'}->{'includes'}}) | ||||
0 | 0 | ||||||
540 | { | ||||||
541 | 0 | 0 | print "\#include \"$include.pm\"\n"; | ||||
542 | } | ||||||
543 | 0 | 0 | print "\n"; | ||||
544 | } | ||||||
545 | |||||||
546 | sub _PrintClassBlock | ||||||
547 | { | ||||||
548 | #** @method private _PrintClassBlock ($sFullClass) | ||||||
549 | # This method will print the class/package block in appropriate doxygen syntax | ||||||
550 | # @param sFullClass - required string (full name of the class) | ||||||
551 | #* | ||||||
552 | 0 | 0 | 0 | my $self = shift; | |||
553 | 0 | 0 | my $sFullClass = shift; | ||||
554 | 0 | 0 | my $logger = $self->GetLogger($self); | ||||
555 | 0 | 0 | $logger->debug("### Entering _PrintClassBlock ###"); | ||||
556 | |||||||
557 | # We need to reset the $1 / $2 match for perl scripts without package classes. | ||||||
558 | # so lets do it here just to be save. Yes this is an expensive way of doing it | ||||||
559 | # but it works. | ||||||
560 | 0 | 0 | $sFullClass =~ /./; | ||||
561 | 0 | 0 | $sFullClass =~ /(.*)\:\:(\w+)$/; | ||||
562 | 0 | 0 | my $parent = $1; | ||||
563 | 0 | 0 | 0 | my $class = $2 || $sFullClass; | |||
564 | |||||||
565 | 0 | 0 | print "/** \@class $sFullClass\n"; | ||||
566 | |||||||
567 | 0 | 0 | my $classDef = $self->{'_hData'}->{'class'}->{$sFullClass}; | ||||
568 | |||||||
569 | 0 | 0 | my $details = $self->{'_hData'}->{'class'}->{$sFullClass}->{'details'}; | ||||
570 | 0 | 0 | 0 | if (defined $details) { print "$details\n"; } | |||
0 | 0 | ||||||
571 | |||||||
572 | 0 | 0 | my $comments = $self->{'_hData'}->{'class'}->{$sFullClass}->{'comments'}; | ||||
573 | 0 | 0 | 0 | if (defined $comments) { print "$comments\n"; } | |||
0 | 0 | ||||||
574 | |||||||
575 | 0 | 0 | print "\@nosubgrouping */\n"; | ||||
576 | |||||||
577 | #if (defined $parent) { print "class $sFullClass : public $parent { \n"; } | ||||||
578 | #else { print "class $sFullClass { \n"; } | ||||||
579 | 0 | 0 | 0 | print "namespace $parent {\n" if ($parent); | |||
580 | 0 | 0 | print "class $class"; | ||||
581 | 0 | 0 | 0 | if (@{$classDef->{inherits}}) | |||
0 | 0 | ||||||
582 | { | ||||||
583 | 0 | 0 | my $count = 0; | ||||
584 | 0 | 0 | foreach my $inherit (@{$classDef->{inherits}}) | ||||
0 | 0 | ||||||
585 | { | ||||||
586 | 0 | 0 | 0 | print(($count++ == 0 ? ": " : ", ")." public ::".$inherit); | |||
587 | } | ||||||
588 | } | ||||||
589 | 0 | 0 | print "\n{\n"; | ||||
590 | 0 | 0 | print "public:\n"; | ||||
591 | } | ||||||
592 | |||||||
593 | sub _PrintMethodBlock | ||||||
594 | { | ||||||
595 | #** @method private _PrintMethodBlock ($class, $methodDef) | ||||||
596 | # This method will print the various subroutines/functions/methods in apprporiate doxygen syntax | ||||||
597 | # @param class - required string (name of the class) | ||||||
598 | # @param state - required string (current state) | ||||||
599 | # @param type - required string (type) | ||||||
600 | # @param method - required string (name of method) | ||||||
601 | #* | ||||||
602 | 0 | 0 | 0 | my $self = shift; | |||
603 | 0 | 0 | my $class = shift; | ||||
604 | 0 | 0 | my $method = shift; | ||||
605 | |||||||
606 | 0 | 0 | my $methodDef = $self->{'_hData'}->{'class'}->{$class}->{'subroutines'}->{$method}; | ||||
607 | |||||||
608 | 0 | 0 | my $state = $methodDef->{state}; | ||||
609 | 0 | 0 | my $type = $methodDef->{type}; | ||||
610 | |||||||
611 | 0 | 0 | my $logger = $self->GetLogger($self); | ||||
612 | 0 | 0 | $logger->debug("### Entering _PrintMethodBlock ###"); | ||||
613 | |||||||
614 | 0 | 0 | 0 | my $returntype = $methodDef->{'returntype'} || $type; | |||
615 | 0 | 0 | 0 | my $parameters = $methodDef->{'parameters'} || ""; | |||
616 | |||||||
617 | 0 | 0 | print "/** \@fn $state $returntype $method\($parameters\)\n"; | ||||
618 | |||||||
619 | 0 | 0 | my $details = $methodDef->{'details'}; | ||||
620 | 0 | 0 | 0 | if (defined $details) { print "$details\n"; } | |||
0 | 0 | ||||||
621 | 0 | 0 | else { print "Undocumented Method\n"; } | ||||
622 | |||||||
623 | 0 | 0 | my $comments = $methodDef->{'comments'}; | ||||
624 | 0 | 0 | 0 | if (defined $comments) { print "$comments\n"; } | |||
0 | 0 | ||||||
625 | |||||||
626 | # Print collapsible source code block | ||||||
627 | 0 | 0 | print "\@htmlonly\n"; | ||||
628 | 0 | 0 | print " \n"; |
||||
629 | 0 | 0 | print "\t![]() |
||||
630 | 0 | 0 | print "\n"; | ||||
631 | 0 | 0 | print " click to view \n"; |
||||
632 | 0 | 0 | print " | ||||
633 | 0 | 0 | print "\@endhtmlonly\n"; | ||||
634 | |||||||
635 | 0 | 0 | print "\@code\n"; | ||||
636 | 0 | 0 | print "\# Number of lines of code in $method: $methodDef->{'length'}\n"; | ||||
637 | 0 | 0 | print "$methodDef->{'code'}\n"; | ||||
638 | 0 | 0 | print "\@endcode \@htmlonly\n"; | ||||
639 | 0 | 0 | print "\n"; | ||||
640 | 0 | 0 | print "\@endhtmlonly */\n"; | ||||
641 | |||||||
642 | 0 | 0 | print "$state $returntype $method\($parameters\)\;\n"; | ||||
643 | } | ||||||
644 | |||||||
645 | sub _ProcessPerlMethod | ||||||
646 | { | ||||||
647 | #** @method private _ProcessPerlMethod ($line) | ||||||
648 | # This method will process the contents of a subroutine/function/method and try to figure out | ||||||
649 | # the name and wether or not it is a private or public method. The private or public status, | ||||||
650 | # if not defined in a doxygen comment block will be determined based on the file name. As with | ||||||
651 | # C and other languages, an "_" should be the first character for all private functions/methods. | ||||||
652 | # @param line - required string (full line of code) | ||||||
653 | #* | ||||||
654 | 0 | 0 | 0 | my $self = shift; | |||
655 | 0 | 0 | my $line = shift; | ||||
656 | 0 | 0 | my $logger = $self->GetLogger($self); | ||||
657 | 0 | 0 | $logger->debug("### Entering _ProcessPerlMethod ###"); | ||||
658 | |||||||
659 | 0 | 0 | my $sClassName = $self->{'_sCurrentClass'}; | ||||
660 | |||||||
661 | 0 | 0 | 0 | if ($line =~ /^\s*sub\s+(.*)/) | |||
662 | { | ||||||
663 | # We should keep track of the order in which the methods were written in the code so we can print | ||||||
664 | # them out in the same order | ||||||
665 | 0 | 0 | my $sName = $1; | ||||
666 | # If they have declared the subrountine with a brace on the same line, lets remove it | ||||||
667 | 0 | 0 | $sName =~ s/\{.*\}?//; | ||||
668 | # Remove any leading or trailing whitespace from the name, just to be safe | ||||||
669 | 0 | 0 | $sName =~ s/\s//g; | ||||
670 | 0 | 0 | $logger->debug("Method Name: $sName"); | ||||
671 | |||||||
672 | 0 | 0 | push (@{$self->{'_hData'}->{'class'}->{$sClassName}->{'subroutineorder'}}, $sName); | ||||
0 | 0 | ||||||
673 | 0 | 0 | $self->{'_sCurrentMethodName'} = $sName; | ||||
674 | } | ||||||
675 | 0 | 0 | my $sMethodName = $self->{'_sCurrentMethodName'}; | ||||
676 | |||||||
677 | # Lets find out if this is a public or private method/function based on a naming standard | ||||||
678 | 0 | 0 | 0 | if ($sMethodName =~ /^_/) { $self->{'_sCurrentMethodState'} = 'private'; } | |||
0 | 0 | ||||||
679 | 0 | 0 | else { $self->{'_sCurrentMethodState'} = 'public'; } | ||||
680 | |||||||
681 | 0 | 0 | my $sMethodState = $self->{'_sCurrentMethodState'}; | ||||
682 | 0 | 0 | $logger->debug("Method State: $sMethodState"); | ||||
683 | |||||||
684 | # We need to count the number of open and close braces so we can see if we are still in a subroutine or not | ||||||
685 | # but we need to becareful so that we do not count braces in comments and braces that are in match patters /\{/ | ||||||
686 | # If there are more open then closed, then we are still in a subroutine | ||||||
687 | 0 | 0 | my $cleanline = $line; | ||||
688 | 0 | 0 | $logger->debug("Cleanline: $cleanline"); | ||||
689 | |||||||
690 | # Remove any comments even those inline with code but not if the hash mark "#" is in a pattern match | ||||||
691 | # unless ($cleanline =~ /=~/) { $cleanline =~ s/#.*$//; } | ||||||
692 | # Patch from Stefan Tauner to address hash marks showing up at the last element of an array, $#array | ||||||
693 | 0 | 0 | 0 | unless ($cleanline =~ /=~/) { $cleanline =~ s/([^\$])#.*$/$1/; } | |||
0 | 0 | ||||||
694 | 0 | 0 | $logger->debug("Cleanline: $cleanline"); | ||||
695 | # Need to remove braces from counting when they are in a pattern match but not when they are supposed to be | ||||||
696 | # there as in the second use case listed below. Below the use cases is some ideas on how to do this. | ||||||
697 | # use case: $a =~ /\{/ | ||||||
698 | # use case: if (/\{/) { foo; } | ||||||
699 | # use case: unless ($cleanline =~ /=~/) { $cleanline =~ s/#.*$//; } | ||||||
700 | 0 | 0 | $cleanline =~ s#/.*?/##g; | ||||
701 | 0 | 0 | $logger->debug("Cleanline: $cleanline"); | ||||
702 | # Remove any braces found in a print statement lile: | ||||||
703 | # use case: print "some foo { bar somethingelse"; | ||||||
704 | # use case: print "$self->{'_hData'}->{'filename'}->{'details'}\n"; | ||||||
705 | 0 | 0 | 0 | if ($cleanline =~ /(.*?print\s*)(.*?);(.*)/) | |||
706 | { | ||||||
707 | 0 | 0 | my $sLineData1 = $1; | ||||
708 | 0 | 0 | my $sLineData2 = $2; | ||||
709 | 0 | 0 | my $sLineData3 = $3; | ||||
710 | 0 | 0 | $sLineData2 =~ s#[{}]##g; | ||||
711 | 0 | 0 | $cleanline = $sLineData1 . $sLineData2. $sLineData3; | ||||
712 | } | ||||||
713 | #$cleanline =~ s/(print\s*\".*){(.*\")/$1$2/g; | ||||||
714 | 0 | 0 | $logger->debug("Cleanline: $cleanline"); | ||||
715 | |||||||
716 | 0 | 0 | $self->{'_iOpenBrace'} += @{[$cleanline =~ /\{/g]}; | ||||
0 | 0 | ||||||
717 | 0 | 0 | $self->{'_iCloseBrace'} += @{[$cleanline =~ /\}/g]}; | ||||
0 | 0 | ||||||
718 | 0 | 0 | $logger->debug("Open Brace Number: $self->{'_iOpenBrace'}"); | ||||
719 | 0 | 0 | $logger->debug("Close Brace Number: $self->{'_iCloseBrace'}"); | ||||
720 | |||||||
721 | |||||||
722 | # Use Case 1: sub foo { return; } | ||||||
723 | # Use Case 2: sub foo {\n} | ||||||
724 | # Use Case 3: sub foo \n {\n } | ||||||
725 | |||||||
726 | 0 | 0 | 0 | 0 | if ($self->{'_iOpenBrace'} > $self->{'_iCloseBrace'}) | ||
0 | |||||||
727 | { | ||||||
728 | # Use Case 2, still in subroutine | ||||||
729 | 0 | 0 | $logger->debug("We are still in the subroutine"); | ||||
730 | } | ||||||
731 | elsif ($self->{'_iOpenBrace'} > 0 && $self->{'_iOpenBrace'} == $self->{'_iCloseBrace'}) | ||||||
732 | { | ||||||
733 | # Use Case 1, we are leaving a subroutine | ||||||
734 | 0 | 0 | $logger->debug("We are leaving the subroutine"); | ||||
735 | 0 | 0 | $self->_ChangeState('NORMAL'); | ||||
736 | 0 | 0 | $self->RESETSUB(); | ||||
737 | } | ||||||
738 | else | ||||||
739 | { | ||||||
740 | # Use Case 3, still in subroutine | ||||||
741 | 0 | 0 | $logger->debug("A subroutine has been started but we are not yet in it as we have yet to see an open brace"); | ||||
742 | } | ||||||
743 | |||||||
744 | # Doxygen makes use of the @ symbol and treats it as a special reserved character. This is a problem for perl | ||||||
745 | # and especailly when we are documenting our own Doxygen code we have print statements that include things like @endcode | ||||||
746 | # as is found in _PrintMethodBlock(). Lets convert those @ to @amp; | ||||||
747 | 0 | 0 | $line =~ s/\@endcode/\&\#64\;endcode/g; | ||||
748 | |||||||
749 | # Record the current line for code output | ||||||
750 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'code'} .= $line; | ||||
751 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'length'}++; | ||||
752 | |||||||
753 | # Only set these values if they were not already set by a comment block outside the subroutine | ||||||
754 | # This is for public/private | ||||||
755 | 0 | 0 | 0 | unless (defined $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'state'}) | |||
756 | { | ||||||
757 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'state'} = $sMethodState; | ||||
758 | } | ||||||
759 | # This is for function/method | ||||||
760 | 0 | 0 | 0 | unless (defined $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'type'}) | |||
761 | { | ||||||
762 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'type'} = "method"; | ||||
763 | } | ||||||
764 | } | ||||||
765 | |||||||
766 | sub _ProcessPodCommentBlock | ||||||
767 | { | ||||||
768 | #** @method private _ProcessPodCommentBlock () | ||||||
769 | # This method will process an entire POD block in one pass, after it has all been gathered by the state machine. | ||||||
770 | #* | ||||||
771 | 0 | 0 | 0 | my $self = shift; | |||
772 | 0 | 0 | my $logger = $self->GetLogger($self); | ||||
773 | 0 | 0 | $logger->debug("### Entering _ProcessPodCommentBlock ###"); | ||||
774 | |||||||
775 | 0 | 0 | my $sClassName = $self->{'_sCurrentClass'}; | ||||
776 | 0 | 0 | my @aBlock = @{$self->{'_aPodBlock'}}; | ||||
0 | 0 | ||||||
777 | |||||||
778 | # Lets clean up the array in the object now that we have a local copy as we will no longer need that. We want to make | ||||||
779 | # sure it is all clean and ready for the next comment block | ||||||
780 | 0 | 0 | $self->RESETPOD(); | ||||
781 | |||||||
782 | 0 | 0 | my $sPodRawText; | ||||
783 | 0 | 0 | foreach (@aBlock) | ||||
784 | { | ||||||
785 | # If we find any Doxygen special characters in the POD, lets escape them | ||||||
786 | 0 | 0 | s/(\@|\\|\%|#)/\\$1/g; | ||||
787 | 0 | 0 | $sPodRawText .= $_; | ||||
788 | } | ||||||
789 | |||||||
790 | 0 | 0 | my $parser = new Pod::POM(); | ||||
791 | 0 | 0 | my $pom = $parser->parse_text($sPodRawText); | ||||
792 | 0 | 0 | my $sPodParsedText = Doxygen::Filter::Perl::POD->print($pom); | ||||
793 | |||||||
794 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'comments'} .= $sPodParsedText; | ||||
795 | } | ||||||
796 | |||||||
797 | |||||||
798 | sub _ProcessDoxygenCommentBlock | ||||||
799 | { | ||||||
800 | #** @method private _ProcessDoxygenCommentBlock () | ||||||
801 | # This method will process an entire comment block in one pass, after it has all been gathered by the state machine | ||||||
802 | #* | ||||||
803 | 0 | 0 | 0 | my $self = shift; | |||
804 | 0 | 0 | my $logger = $self->GetLogger($self); | ||||
805 | 0 | 0 | $logger->debug("### Entering _ProcessDoxygenCommentBlock ###"); | ||||
806 | |||||||
807 | 0 | 0 | my @aBlock = @{$self->{'_aDoxygenBlock'}}; | ||||
0 | 0 | ||||||
808 | |||||||
809 | # Lets clean up the array in the object now that we have a local copy as we will no longer need that. We want to make | ||||||
810 | # sure it is all clean and ready for the next comment block | ||||||
811 | 0 | 0 | $self->RESETDOXY(); | ||||
812 | |||||||
813 | 0 | 0 | my $sClassName = $self->{'_sCurrentClass'}; | ||||
814 | 0 | 0 | my $sSubState = ''; | ||||
815 | 0 | 0 | $logger->debug("We are currently in class $sClassName"); | ||||
816 | |||||||
817 | # Lets grab the command line and put it in a variable for easier use | ||||||
818 | 0 | 0 | my $sCommandLine = $aBlock[0]; | ||||
819 | 0 | 0 | $logger->debug("The command line for this doxygen comment is $sCommandLine"); | ||||
820 | |||||||
821 | 0 | 0 | $sCommandLine =~ /^\s*#\*\*\s+\@([\w:]+)\s+(.*)/; | ||||
822 | 0 | 0 | my $sCommand = lc($1); | ||||
823 | 0 | 0 | my $sOptions = $2; | ||||
824 | 0 | 0 | $logger->debug("Command: $sCommand"); | ||||
825 | 0 | 0 | $logger->debug("Options: $sOptions"); | ||||
826 | |||||||
827 | # If the user entered @fn instead of @function, lets change it | ||||||
828 | 0 | 0 | 0 | if ($sCommand eq "fn") { $sCommand = "function"; } | |||
0 | 0 | ||||||
829 | |||||||
830 | # Lets find out what doxygen sub state we should be in | ||||||
831 | 0 | 0 | 0 | if ($sCommand eq 'file') { $sSubState = 'DOXYFILE'; } | |||
0 | 0 | 0 | |||||
0 | |||||||
0 | |||||||
0 | |||||||
0 | |||||||
0 | |||||||
832 | 0 | 0 | elsif ($sCommand eq 'class') { $sSubState = 'DOXYCLASS'; } | ||||
833 | 0 | 0 | elsif ($sCommand eq 'package') { $sSubState = 'DOXYCLASS'; } | ||||
834 | 0 | 0 | elsif ($sCommand eq 'function') { $sSubState = 'DOXYFUNCTION'; } | ||||
835 | 0 | 0 | elsif ($sCommand eq 'method') { $sSubState = 'DOXYMETHOD'; } | ||||
836 | 0 | 0 | elsif ($sCommand eq 'attr') { $sSubState = 'DOXYATTR'; } | ||||
837 | 0 | 0 | elsif ($sCommand eq 'var') { $sSubState = 'DOXYATTR'; } | ||||
838 | 0 | 0 | else { $sSubState = 'DOXYCOMMENT'; } | ||||
839 | 0 | 0 | $logger->debug("Substate is now $sSubState"); | ||||
840 | |||||||
841 | 0 | 0 | 0 | 0 | if ($sSubState eq 'DOXYFILE' ) | ||
0 | |||||||
0 | |||||||
0 | |||||||
0 | |||||||
842 | { | ||||||
843 | 0 | 0 | $logger->debug("Processing a Doxygen file object"); | ||||
844 | # We need to remove the command line from this block | ||||||
845 | 0 | 0 | shift @aBlock; | ||||
846 | 0 | 0 | $self->{'_hData'}->{'filename'}->{'details'} = $self->_RemovePerlCommentFlags(\@aBlock); | ||||
847 | } | ||||||
848 | elsif ($sSubState eq 'DOXYCLASS') | ||||||
849 | { | ||||||
850 | 0 | 0 | $logger->debug("Processing a Doxygen class object"); | ||||
851 | #my $sClassName = $sOptions; | ||||||
852 | 0 | 0 | 0 | my $sClassName = $sOptions || $sClassName; | |||
853 | 0 | 0 | my $classDef = $self->_SwitchClass($sClassName); | ||||
854 | # We need to remove the command line from this block | ||||||
855 | 0 | 0 | shift @aBlock; | ||||
856 | #$self->{'_hData'}->{'class'}->{$sClassName}->{'details'} = $self->_RemovePerlCommentFlags(\@aBlock); | ||||||
857 | 0 | 0 | $classDef->{'details'} = $self->_RemovePerlCommentFlags(\@aBlock); | ||||
858 | } | ||||||
859 | elsif ($sSubState eq 'DOXYCOMMENT') | ||||||
860 | { | ||||||
861 | 0 | 0 | $logger->debug("Processing a Doxygen class object"); | ||||
862 | # For extra comment blocks we need to add the command and option line back to the front of the array | ||||||
863 | 0 | 0 | my $sMethodName = $self->{'_sCurrentMethodName'}; | ||||
864 | 0 | 0 | 0 | if (defined $sMethodName) | |||
865 | { | ||||||
866 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'comments'} .= "\n"; | ||||
867 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'comments'} .= $self->_RemovePerlCommentFlags(\@aBlock); | ||||
868 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'comments'} .= "\n"; | ||||
869 | } | ||||||
870 | else | ||||||
871 | { | ||||||
872 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'comments'} .= "\n"; | ||||
873 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'comments'} .= $self->_RemovePerlCommentFlags(\@aBlock); | ||||
874 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'comments'} .= "\n"; | ||||
875 | } | ||||||
876 | } | ||||||
877 | elsif ($sSubState eq 'DOXYATTR') | ||||||
878 | { | ||||||
879 | # Process the doxygen header first then loop through the rest of the comments | ||||||
880 | #my ($sState, $sAttrName, $sComments) = ($sOptions =~ /(?:(public|private)\s+)?([\$@%\*][\w:]+)\s+(.*)/); | ||||||
881 | 0 | 0 | my ($sState, $modifiers, $modifiersLoop, $modifiersChoice, $fullSpec, $typeSpec, $typeName, $typeLoop, $pointerLoop, $typeCode, $sAttrName, $sComments) = ($sOptions =~ /(?:(public|protected|private)\s+)?(((static|const)\s+)*)((((\w+::)*\w+(\s+|\s*\*+\s+|\s+\*+\s*))|)([\$@%\*])([\w:]+))\s+(.*)/); | ||||
882 | 0 | 0 | 0 | if (defined $sAttrName) | |||
883 | { | ||||||
884 | 0 | 0 | 0 | my $attrDef = $self->{'_hData'}->{'class'}->{$sClassName}->{'attributes'}->{$sAttrName} ||= {}; | |||
885 | 0 | 0 | 0 | if ($typeName) | |||
886 | { | ||||||
887 | 0 | 0 | $attrDef->{'type'} = $typeName; | ||||
888 | } | ||||||
889 | else | ||||||
890 | { | ||||||
891 | 0 | 0 | $attrDef->{'type'} = $self->_ConvertTypeCode($typeCode); | ||||
892 | } | ||||||
893 | 0 | 0 | 0 | if (defined $sState) | |||
894 | { | ||||||
895 | 0 | 0 | $attrDef->{'state'} = $sState; | ||||
896 | } | ||||||
897 | 0 | 0 | 0 | if (defined $sComments) | |||
898 | { | ||||||
899 | 0 | 0 | $attrDef->{'comments'} = $sComments; | ||||
900 | } | ||||||
901 | 0 | 0 | 0 | if (defined $modifiers) | |||
902 | { | ||||||
903 | 0 | 0 | $attrDef->{'modifiers'} = $modifiers; | ||||
904 | } | ||||||
905 | ## We need to remove the command line from this block | ||||||
906 | 0 | 0 | shift @aBlock; | ||||
907 | 0 | 0 | $attrDef->{'details'} = $self->_RemovePerlCommentFlags(\@aBlock); | ||||
908 | 0 | 0 | push(@{$self->GetCurrentClass()->{attributeorder}}, $sAttrName); | ||||
0 | 0 | ||||||
909 | } | ||||||
910 | else | ||||||
911 | { | ||||||
912 | 0 | 0 | $self->ReportError("invalid syntax for attribute: $sOptions\n"); | ||||
913 | } | ||||||
914 | } # End DOXYATTR | ||||||
915 | elsif ($sSubState eq 'DOXYFUNCTION' || $sSubState eq 'DOXYMETHOD') | ||||||
916 | { | ||||||
917 | # Process the doxygen header first then loop through the rest of the comments | ||||||
918 | 0 | 0 | $sOptions =~ /^(.*?)\s*\(\s*(.*?)\s*\)/; | ||||
919 | 0 | 0 | $sOptions = $1; | ||||
920 | 0 | 0 | my $sParameters = $2; | ||||
921 | |||||||
922 | 0 | 0 | my @aOptions; | ||||
923 | my $state; | ||||||
924 | 0 | 0 | my $sMethodName; | ||||
925 | |||||||
926 | 0 | 0 | 0 | if (defined $sOptions) | |||
927 | { | ||||||
928 | 0 | 0 | @aOptions = split(/\s+/, $sOptions); | ||||
929 | # State = Public/Private | ||||||
930 | 0 | 0 | 0 | 0 | if ($aOptions[0] eq "public" || $aOptions[0] eq "private" || $aOptions[0] eq "protected") | ||
0 | |||||||
931 | { | ||||||
932 | 0 | 0 | $state = shift @aOptions; | ||||
933 | } | ||||||
934 | 0 | 0 | $sMethodName = pop(@aOptions); | ||||
935 | } | ||||||
936 | |||||||
937 | 0 | 0 | 0 | 0 | if ($sSubState eq "DOXYFUNCTION" && !grep(/^static$/, @aOptions)) | ||
938 | { | ||||||
939 | 0 | 0 | unshift(@aOptions, "static"); | ||||
940 | } | ||||||
941 | |||||||
942 | 0 | 0 | 0 | unless (defined $sMethodName) | |||
943 | { | ||||||
944 | # If we are already in a subroutine and a user uses sloppy documentation and only does | ||||||
945 | # #**@method in side the subroutine, then lets pull the current method name from the object. | ||||||
946 | # If there is no method defined there, we should die. | ||||||
947 | 0 | 0 | 0 | if (defined $self->{'_sCurrentMethodName'}) { $sMethodName = $self->{'_sCurrentMethodName'}; } | |||
0 | 0 | ||||||
948 | 0 | 0 | else { die "Missing method name in $sCommand syntax"; } | ||||
949 | } | ||||||
950 | |||||||
951 | # If we are not yet in a subroutine, lets keep track that we are now processing a subroutine and its name | ||||||
952 | 0 | 0 | 0 | unless (defined $self->{'_sCurrentMethodName'}) { $self->{'_sCurrentMethodName'} = $sMethodName; } | |||
0 | 0 | ||||||
953 | |||||||
954 | 0 | 0 | 0 | if (defined $sParameters) { $sParameters = $self->_ConvertParameters($sParameters); } | |||
0 | 0 | ||||||
955 | |||||||
956 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'returntype'} = join(" ", @aOptions); | ||||
957 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'type'} = $sCommand; | ||||
958 | 0 | 0 | 0 | if (defined $state) | |||
959 | { | ||||||
960 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'state'} = $state; | ||||
961 | } | ||||||
962 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'parameters'} = $sParameters; | ||||
963 | # We need to remove the command line from this block | ||||||
964 | 0 | 0 | shift @aBlock; | ||||
965 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'details'} = $self->_RemovePerlCommentFlags(\@aBlock); | ||||
966 | |||||||
967 | } # End DOXYFUNCTION || DOXYMETHOD | ||||||
968 | } | ||||||
969 | |||||||
970 | sub _RemovePerlCommentFlags | ||||||
971 | { | ||||||
972 | #** @method private _RemovePerlCommentFlags ($aBlock) | ||||||
973 | # This method will remove all of the comment marks "#" for our output to Doxygen. If the line is | ||||||
974 | # flagged for verbatim then lets not do anything. | ||||||
975 | # @param aBlock - required array_ref (doxygen comment as an array of code lines) | ||||||
976 | # @retval sBlockDetails - string (doxygen comments in one long string) | ||||||
977 | #* | ||||||
978 | 0 | 0 | 0 | my $self = shift; | |||
979 | 0 | 0 | my $aBlock = shift; | ||||
980 | 0 | 0 | my $logger = $self->GetLogger($self); | ||||
981 | 0 | 0 | $logger->debug("### Entering _RemovePerlCommentFlags ###"); | ||||
982 | |||||||
983 | 0 | 0 | my $sBlockDetails = ""; | ||||
984 | 0 | 0 | my $iInVerbatimBlock = 0; | ||||
985 | 0 | 0 | foreach my $line (@$aBlock) | ||||
986 | { | ||||||
987 | # Lets check for a verbatim command option like '# @verbatim' | ||||||
988 | 0 | 0 | 0 | if ($line =~ /^\s*#\s*\@verbatim/) | |||
0 | |||||||
989 | { | ||||||
990 | 0 | 0 | $logger->debug("Found verbatim command"); | ||||
991 | # We need to remove the comment marker from the '# @verbaim' line now since it will not be caught later | ||||||
992 | 0 | 0 | $line =~ s/^\s*#\s*/ /; | ||||
993 | 0 | 0 | $iInVerbatimBlock = 1; | ||||
994 | } | ||||||
995 | elsif ($line =~ /^\s*#\s*\@endverbatim/) | ||||||
996 | { | ||||||
997 | 0 | 0 | $logger->debug("Found endverbatim command"); | ||||
998 | 0 | 0 | $iInVerbatimBlock = 0; | ||||
999 | } | ||||||
1000 | # Lets remove any doxygen command initiator | ||||||
1001 | 0 | 0 | $line =~ s/^\s*#\*\*\s*//; | ||||
1002 | # Lets remove any doxygen command terminators | ||||||
1003 | 0 | 0 | $line =~ s/^\s*#\*\s*//; | ||||
1004 | # Lets remove all of the Perl comment markers so long as we are not in a verbatim block | ||||||
1005 | # if ($iInVerbatimBlock == 0) { $line =~ s/^\s*#+//; } | ||||||
1006 | # Patch from Sebastian Rose to address spacing and indentation in code examples | ||||||
1007 | 0 | 0 | 0 | if ($iInVerbatimBlock == 0) { $line =~ s/^\s*#\s?//; } | |||
0 | 0 | ||||||
1008 | 0 | 0 | $logger->debug("code: $line"); | ||||
1009 | # Patch from Mihai MOJE to address method comments all on the same line. | ||||||
1010 | 0 | 0 | $sBlockDetails .= $line . " "; |
||||
1011 | #$sBlockDetails .= $line; | ||||||
1012 | } | ||||||
1013 | 0 | 0 | $sBlockDetails =~ s/^([ \t]*\n)+//s; | ||||
1014 | 0 | 0 | chomp($sBlockDetails); | ||||
1015 | 0 | 0 | 0 | if ($sBlockDetails) | |||
1016 | { | ||||||
1017 | 0 | 0 | $sBlockDetails =~ s/^/ \*/gm; | ||||
1018 | 0 | 0 | $sBlockDetails .= "\n"; | ||||
1019 | } | ||||||
1020 | 0 | 0 | return $sBlockDetails; | ||||
1021 | } | ||||||
1022 | |||||||
1023 | sub _ConvertToOfficialDoxygenSyntax | ||||||
1024 | { | ||||||
1025 | #** @method private _ConvertToOfficialDoxygenSyntax ($line) | ||||||
1026 | # This method will check the current line for various unsupported doxygen comment blocks and convert them | ||||||
1027 | # to the type we support, '#** @command'. The reason for this is so that we do not need to add them in | ||||||
1028 | # every if statement throughout the code. | ||||||
1029 | # @param line - required string (line of code) | ||||||
1030 | # @retval line - string (line of code) | ||||||
1031 | #* | ||||||
1032 | 7 | 7 | 8 | my $self = shift; | |||
1033 | 7 | 6 | my $line = shift; | ||||
1034 | 7 | 12 | my $logger = $self->GetLogger($self); | ||||
1035 | 7 | 329 | $logger->debug("### Entering _ConvertToOfficialDoxygenSyntax ###"); | ||||
1036 | |||||||
1037 | # This will match "## @command" and convert it to "#** @command" | ||||||
1038 | 7 | 50 | 40 | if ($line =~ /^\s*##\s+\@/) { $line =~ s/^(\s*)##(\s+\@)/$1#\*\*$2/; } | |||
0 | 0 | ||||||
1039 | else { | ||||||
1040 | 7 | 10 | $logger->debug('Nothing to do, did not find any ## @'); | ||||
1041 | } | ||||||
1042 | 7 | 34 | return $line; | ||||
1043 | } | ||||||
1044 | |||||||
1045 | sub _ConvertTypeCode | ||||||
1046 | { | ||||||
1047 | #** @method private _ConvertTypeCode($code) | ||||||
1048 | # This method will change the $, @, and %, etc to written names so that Doxygen does not have a problem with them | ||||||
1049 | # @param code | ||||||
1050 | # required prefix of variable | ||||||
1051 | #* | ||||||
1052 | 0 | 0 | my $self = shift; | ||||
1053 | 0 | my $code = shift; | |||||
1054 | 0 | my $logger = $self->GetLogger($self); | |||||
1055 | 0 | $logger->debug("### Entering _ConvertParameters ###"); | |||||
1056 | |||||||
1057 | # Lets clean up the parameters list so that it will work with Doxygen | ||||||
1058 | 0 | $code =~ s/\$\$/scalar_ref/g; | |||||
1059 | 0 | $code =~ s/\@\$/array_ref/g; | |||||
1060 | 0 | $code =~ s/\%\$/hash_ref/g; | |||||
1061 | 0 | $code =~ s/\$/scalar/g; | |||||
1062 | 0 | $code =~ s/\@/array/g; | |||||
1063 | 0 | $code =~ s/\%/hash/g; | |||||
1064 | |||||||
1065 | 0 | return $code; | |||||
1066 | } | ||||||
1067 | |||||||
1068 | sub _ConvertParameters | ||||||
1069 | { | ||||||
1070 | #** @method private _ConvertParameters () | ||||||
1071 | # This method will change the $, @, and %, etc to written names so that Doxygen does not have a problem with them | ||||||
1072 | # @param sParameters - required string (variable parameter to change) | ||||||
1073 | #* | ||||||
1074 | 0 | 0 | my $self = shift; | ||||
1075 | 0 | my $sParameters = shift; | |||||
1076 | 0 | my $logger = $self->GetLogger($self); | |||||
1077 | 0 | $logger->debug("### Entering _ConvertParameters ###"); | |||||
1078 | |||||||
1079 | # Lets clean up the parameters list so that it will work with Doxygen | ||||||
1080 | 0 | $sParameters =~ s/\$\$/scalar_ref /g; | |||||
1081 | 0 | $sParameters =~ s/\@\$/array_ref /g; | |||||
1082 | 0 | $sParameters =~ s/\%\$/hash_ref /g; | |||||
1083 | 0 | $sParameters =~ s/\$/scalar /g; | |||||
1084 | 0 | $sParameters =~ s/\@/array /g; | |||||
1085 | 0 | $sParameters =~ s/\%/hash /g; | |||||
1086 | |||||||
1087 | 0 | return $sParameters; | |||||
1088 | } | ||||||
1089 | |||||||
1090 | =head1 NAME | ||||||
1091 | |||||||
1092 | Doxygen::Filter::Perl - A perl code pre-filter for Doxygen | ||||||
1093 | |||||||
1094 | =head1 DESCRIPTION | ||||||
1095 | |||||||
1096 | The Doxygen::Filter::Perl module is designed to provide support for documenting | ||||||
1097 | perl scripts and modules to be used with the Doxygen engine. We plan on | ||||||
1098 | supporting most Doxygen style comments and POD (plain old documentation) style | ||||||
1099 | comments. The Doxgyen style comment blocks for methods/functions can be inside | ||||||
1100 | or outside the method/function. Doxygen::Filter::Perl is hosted at | ||||||
1101 | http://perldoxygen.sourceforge.net/ | ||||||
1102 | |||||||
1103 | =head1 USAGE | ||||||
1104 | |||||||
1105 | Install Doxygen::Filter::Perl via CPAN or from source. If you install from | ||||||
1106 | source then do: | ||||||
1107 | |||||||
1108 | perl Makefile.PL | ||||||
1109 | make | ||||||
1110 | make install | ||||||
1111 | |||||||
1112 | Make sure that the doxygen-filter-perl script was copied from this project into | ||||||
1113 | your path somewhere and that it has RX permissions. Example: | ||||||
1114 | |||||||
1115 | /usr/local/bin/doxygen-filter-perl | ||||||
1116 | |||||||
1117 | Copy over the Doxyfile file from this project into the root directory of your | ||||||
1118 | project so that it is at the same level as your lib directory. This file will | ||||||
1119 | have all of the presets needed for documenting Perl code. You can edit this | ||||||
1120 | file with the doxywizard tool if you so desire or if you need to change the | ||||||
1121 | lib directory location or the output location (the default output is ./doc). | ||||||
1122 | Please see the Doxygen manual for information on how to configure the Doxyfile | ||||||
1123 | via a text editor or with the doxywizard tool. | ||||||
1124 | Example: | ||||||
1125 | |||||||
1126 | /home/jordan/workspace/PerlDoxygen/trunk/Doxyfile | ||||||
1127 | /home/jordan/workspace/PerlDoxygen/trunk/lib/Doxygen/Filter/Perl.pm | ||||||
1128 | |||||||
1129 | Once you have done this you can simply run the following from the root of your | ||||||
1130 | project to document your Perl scripts or methods. Example: | ||||||
1131 | |||||||
1132 | /home/jordan/workspace/PerlDoxygen/trunk/> doxygen Doxyfile | ||||||
1133 | |||||||
1134 | All of your documentation will be in the ./doc/html/ directory inside of your | ||||||
1135 | project root. | ||||||
1136 | |||||||
1137 | =head1 DOXYGEN SUPPORT | ||||||
1138 | |||||||
1139 | The following Doxygen style comment is the preferred block style, though others | ||||||
1140 | are supported and are listed below: | ||||||
1141 | |||||||
1142 | #** | ||||||
1143 | # ........ | ||||||
1144 | #* | ||||||
1145 | |||||||
1146 | You can also start comment blocks with "##" and end comment blocks with a blank | ||||||
1147 | line or real code, this allows you to place comments right next to the | ||||||
1148 | subroutines that they refer to if you wish. A comment block must have | ||||||
1149 | continuous "#" comment markers as a blank line can be used as a termination | ||||||
1150 | mark for the doxygen comment block. | ||||||
1151 | |||||||
1152 | In other languages the Doxygen @fn structural indicator is used to document | ||||||
1153 | subroutines/functions/methods and the parsing engine figures out what is what. | ||||||
1154 | In Perl that is a lot harder to do so I have added a @method and @function | ||||||
1155 | structural indicator so that they can be documented seperatly. | ||||||
1156 | |||||||
1157 | =head2 Supported Structural Indicators | ||||||
1158 | |||||||
1159 | #** @file [filename] | ||||||
1160 | # ........ | ||||||
1161 | #* | ||||||
1162 | |||||||
1163 | #** @class [class name (ex. Doxygen::Filter::Perl)] | ||||||
1164 | # ........ | ||||||
1165 | #* | ||||||
1166 | |||||||
1167 | #** @method or @function [public|protected|private] [method-name] (parameters) | ||||||
1168 | # ........ | ||||||
1169 | #* | ||||||
1170 | |||||||
1171 | #** @attr or @var [public|protected|private] [type] {$%@}[attribute-name] [brief description] | ||||||
1172 | # ........ | ||||||
1173 | #* | ||||||
1174 | |||||||
1175 | #** @section [section-name] [section-title] | ||||||
1176 | # ........ | ||||||
1177 | #* | ||||||
1178 | |||||||
1179 | #** @brief [notes] | ||||||
1180 | # ........ | ||||||
1181 | #* | ||||||
1182 | |||||||
1183 | =head2 Support Style Options and Section Indicators | ||||||
1184 | |||||||
1185 | All doxygen style options and section indicators are supported inside the | ||||||
1186 | structural indicators that we currently support. | ||||||
1187 | |||||||
1188 | =head2 Documenting Subroutines/Functions/Methods | ||||||
1189 | |||||||
1190 | The Doxygen style comment blocks that describe a function or method can | ||||||
1191 | exist before, after, or inside the subroutine that it is describing. Examples | ||||||
1192 | are listed below. It is also important to note that you can leave the public/private | ||||||
1193 | out and the filter will guess based on the subroutine name. The normal convention | ||||||
1194 | in other languages like C is to have the function/method start with an "_" if it | ||||||
1195 | is private/protected. We do the same thing here even though there is really no | ||||||
1196 | such thing in Perl. The whole reason for this is to help users of the code know | ||||||
1197 | what functions they should call directly and which they should not. The generic | ||||||
1198 | documentation blocks for functions and methods look like: | ||||||
1199 | |||||||
1200 | #** @function [public|protected|private] [return-type] function-name (parameters) | ||||||
1201 | # @brief A brief description of the function | ||||||
1202 | # | ||||||
1203 | # A detailed description of the function | ||||||
1204 | # @params value [required|optional] [details] | ||||||
1205 | # @retval value [details] | ||||||
1206 | # .... | ||||||
1207 | #* | ||||||
1208 | |||||||
1209 | #** @method [public|protected|private] [return-type] method-name (parameters) | ||||||
1210 | # @brief A brief description of the method | ||||||
1211 | # | ||||||
1212 | # A detailed description of the method | ||||||
1213 | # @params value [required|optional] [details] | ||||||
1214 | # @retval value [details] | ||||||
1215 | # .... | ||||||
1216 | #* | ||||||
1217 | |||||||
1218 | The parameters would normally be something like $foo, @bar, or %foobar. I have | ||||||
1219 | also added support for scalar, array, and hash references and those would be | ||||||
1220 | documented as $$foo, @$bar, %$foobar. An example would look this: | ||||||
1221 | |||||||
1222 | #** @method public ProcessDataValues ($$sFile, %$hDataValues) | ||||||
1223 | |||||||
1224 | =head2 Function / Method Example | ||||||
1225 | |||||||
1226 | sub test1 | ||||||
1227 | { | ||||||
1228 | #** @method public test1 ($value) | ||||||
1229 | # .... | ||||||
1230 | #* | ||||||
1231 | } | ||||||
1232 | |||||||
1233 | #** @method public test2 ($value) | ||||||
1234 | # .... | ||||||
1235 | #* | ||||||
1236 | sub test2 | ||||||
1237 | { | ||||||
1238 | |||||||
1239 | } | ||||||
1240 | |||||||
1241 | =head1 DATA STRUCTURE | ||||||
1242 | |||||||
1243 | $self->{'_hData'}->{'filename'}->{'fullpath'} = string | ||||||
1244 | $self->{'_hData'}->{'filename'}->{'shortname'} = string | ||||||
1245 | $self->{'_hData'}->{'filename'}->{'version'} = string | ||||||
1246 | $self->{'_hData'}->{'filename'}->{'details'} = string | ||||||
1247 | $self->{'_hData'}->{'includes'} = array | ||||||
1248 | |||||||
1249 | $self->{'_hData'}->{'class'}->{'classorder'} = array | ||||||
1250 | $self->{'_hData'}->{'class'}->{$class}->{'subroutineorder'} = array | ||||||
1251 | $self->{'_hData'}->{'class'}->{$class}->{'attributeorder'} = array | ||||||
1252 | $self->{'_hData'}->{'class'}->{$class}->{'details'} = string | ||||||
1253 | $self->{'_hData'}->{'class'}->{$class}->{'comments'} = string | ||||||
1254 | |||||||
1255 | $self->{'_hData'}->{'class'}->{$class}->{'subroutines'}->{$method}->{'type'} = string (method / function) | ||||||
1256 | $self->{'_hData'}->{'class'}->{$class}->{'subroutines'}->{$method}->{'returntype'} = string (return type) | ||||||
1257 | $self->{'_hData'}->{'class'}->{$class}->{'subroutines'}->{$method}->{'state'} = string (public / private) | ||||||
1258 | $self->{'_hData'}->{'class'}->{$class}->{'subroutines'}->{$method}->{'parameters'} = string (method / function parameters) | ||||||
1259 | $self->{'_hData'}->{'class'}->{$class}->{'subroutines'}->{$method}->{'code'} = string | ||||||
1260 | $self->{'_hData'}->{'class'}->{$class}->{'subroutines'}->{$method}->{'length'} = integer | ||||||
1261 | $self->{'_hData'}->{'class'}->{$class}->{'subroutines'}->{$method}->{'details'} = string | ||||||
1262 | $self->{'_hData'}->{'class'}->{$class}->{'subroutines'}->{$method}->{'comments'} = string | ||||||
1263 | |||||||
1264 | $self->{'_hData'}->{'class'}->{$class}->{'attributes'}->{$variable}->{'state'} = string (public / private) | ||||||
1265 | $self->{'_hData'}->{'class'}->{$class}->{'attributes'}->{$variable}->{'modifiers'} = string | ||||||
1266 | $self->{'_hData'}->{'class'}->{$class}->{'attributes'}->{$variable}->{'comments'} = string | ||||||
1267 | $self->{'_hData'}->{'class'}->{$class}->{'attributes'}->{$variable}->{'details'} = string | ||||||
1268 | |||||||
1269 | =head1 AUTHOR | ||||||
1270 | |||||||
1271 | Bret Jordan |
||||||
1272 | |||||||
1273 | =head1 LICENSE | ||||||
1274 | |||||||
1275 | Doxygen::Filter::Perl is licensed with an Apache 2 license. See the LICENSE | ||||||
1276 | file for more details. | ||||||
1277 | |||||||
1278 | =cut | ||||||
1279 | |||||||
1280 | return 1; |