lib/Doxygen/Filter/Perl.pm | |||
---|---|---|---|
Criterion | Covered | Total | % |
statement | 95 | 502 | 18.9 |
branch | 14 | 182 | 7.6 |
condition | 3 | 60 | 5.0 |
subroutine | 20 | 36 | 55.5 |
pod | 0 | 11 | 0.0 |
total | 132 | 791 | 16.6 |
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 2014-03-17 13:08:02Z jordan2175 $ | ||||||
22 | #* | ||||||
23 | package Doxygen::Filter::Perl; | ||||||
24 | |||||||
25 | 1 | 1 | 2461 | use 5.8.8; | |||
1 | 4 | ||||||
1 | 57 | ||||||
26 | 1 | 1 | 5 | use strict; | |||
1 | 1 | ||||||
1 | 36 | ||||||
27 | 1 | 1 | 6 | use warnings; | |||
1 | 6 | ||||||
1 | 37 | ||||||
28 | 1 | 1 | 610 | use parent qw(Doxygen::Filter); | |||
1 | 385 | ||||||
1 | 6 | ||||||
29 | 1 | 1 | 71 | use Log::Log4perl; | |||
1 | 2 | ||||||
1 | 5 | ||||||
30 | 1 | 1 | 1042 | use Pod::POM; | |||
1 | 22033 | ||||||
1 | 68 | ||||||
31 | 1 | 1 | 714 | use IO::Handle; | |||
1 | 6310 | ||||||
1 | 74 | ||||||
32 | 1 | 1 | 469 | use Doxygen::Filter::Perl::POD; | |||
1 | 3 | ||||||
1 | 5999 | ||||||
33 | |||||||
34 | our $VERSION = '1.70'; | ||||||
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 | 10 | my $pkg = shift; | ||
70 | 1 | 33 | 6 | 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 | 21 | $self->_init(@_); | ||||
77 | 1 | 4 | return $self; | ||||
78 | } | ||||||
79 | |||||||
80 | sub DESTROY | ||||||
81 | { | ||||||
82 | #** @method private DESTROY () | ||||||
83 | # This is the destructor | ||||||
84 | #* | ||||||
85 | 1 | 1 | 352 | my $self = shift; | |||
86 | 1 | 29 | $self = {}; | ||||
87 | } | ||||||
88 | |||||||
89 | sub RESETSUB | ||||||
90 | { | ||||||
91 | 1 | 1 | 0 | 2 | my $self = shift; | ||
92 | 1 | 2 | $self->{'_iOpenBrace'} = 0; | ||||
93 | 1 | 2 | $self->{'_iCloseBrace'} = 0; | ||||
94 | 1 | 3 | $self->{'_sCurrentMethodName'} = undef; | ||||
95 | 1 | 1 | $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 | 2 | my $self = shift; | ||
104 | #$self->{'_sCurrentClass'} = 'main'; | ||||||
105 | #push (@{$self->{'_hData'}->{'class'}->{'classorder'}}, 'main'); | ||||||
106 | 1 | 3 | $self->_SwitchClass('main'); | ||||
107 | } | ||||||
108 | |||||||
109 | 1 | 1 | 0 | 2 | sub RESETDOXY { shift->{'_aDoxygenBlock'} = []; } | ||
110 | 1 | 1 | 0 | 1 | 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 | 2 | my $self = shift; | |||
121 | 1 | 6 | $self->{'_iDebug'} = 0; | ||||
122 | 1 | 2 | $self->{'_sState'} = undef; | ||||
123 | 1 | 3 | $self->{'_sPreviousState'} = []; | ||||
124 | 1 | 3 | $self->_ChangeState('NORMAL'); | ||||
125 | 1 | 2 | $self->{'_hData'} = {}; | ||||
126 | 1 | 4 | $self->RESETFILE(); | ||||
127 | 1 | 3 | $self->RESETCLASS(); | ||||
128 | 1 | 3 | $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 | 863 | my $self = shift; | ||
200 | 3 | 6 | my $logger = $self->GetLogger($self); | ||||
201 | 3 | 265 | $logger->debug("### Entering ProcessFile ###"); | ||||
202 | |||||||
203 | 3 | 16 | $self->{'_hData'}->{'lineno'} = 0; | ||||
204 | 3 | 4 | foreach my $line (@{$self->{'_aRawFileData'}}) | ||||
3 | 7 | ||||||
205 | { | ||||||
206 | 7 | 9 | $self->{'_hData'}->{'lineno'}++; | ||||
207 | # Convert syntax block header to supported doxygen form, if this line is a header | ||||||
208 | 7 | 11 | $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 | 18 | if ($self->{'_sState'} eq 'NORMAL') | |||
0 | |||||||
0 | |||||||
0 | |||||||
214 | { | ||||||
215 | 7 | 11 | $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 | 10 | if ($self->{'_sState'} eq 'NORMAL') | |||
0 | |||||||
0 | |||||||
0 | |||||||
260 | { | ||||||
261 | 7 | 50 | 42 | 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 | 4 | my $version = $1; | ||||
272 | 2 | 9 | $version =~ s/[\'\"\(\)\;]//g; | ||||
273 | 2 | 9 | $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 | 0 | 0 | foreach my $methodName (@{$self->{'_hData'}->{'class'}->{$class}->{'subroutineorder'}}) | ||||
0 | 0 | ||||||
429 | { | ||||||
430 | 0 | 0 | $self->_PrintMethodBlock($class, $methodName); | ||||
431 | } | ||||||
432 | # Print end of class mark | ||||||
433 | 0 | 0 | print "}\;\n"; | ||||
434 | # print end of namespace if class is nested | ||||||
435 | 0 | 0 | 0 | print "};\n" if ($class =~ /::/); | |||
436 | } | ||||||
437 | } | ||||||
438 | |||||||
439 | |||||||
440 | # ---------------------------------------- | ||||||
441 | # Private Methods | ||||||
442 | # ---------------------------------------- | ||||||
443 | 0 | 0 | 0 | sub _FilterOutSystemPackages { return grep({ !exists $SYSTEM_PACKAGES{$_} } @_); } | |||
0 | 0 | ||||||
444 | |||||||
445 | sub _SwitchClass | ||||||
446 | { | ||||||
447 | 1 | 1 | 1 | my $self = shift; | |||
448 | 1 | 6 | my $class = shift; | ||||
449 | |||||||
450 | 1 | 1 | $self->{'_sCurrentClass'} = $class; | ||||
451 | 1 | 50 | 5 | if (!exists $self->{'_hData'}->{'class'}->{$class}) | |||
452 | { | ||||||
453 | 1 | 1 | push(@{$self->{'_hData'}->{'class'}->{'classorder'}}, $class); | ||||
1 | 4 | ||||||
454 | 1 | 5 | $self->{'_hData'}->{'class'}->{$class} = { | ||||
455 | classname => $class, | ||||||
456 | inherits => [], | ||||||
457 | attributeorder => [], | ||||||
458 | subroutineorder => [], | ||||||
459 | }; | ||||||
460 | } | ||||||
461 | |||||||
462 | 1 | 2 | return $self->{'_hData'}->{'class'}->{$class}; | ||||
463 | } | ||||||
464 | |||||||
465 | 0 | 0 | 0 | sub _RestoreState { shift->_ChangeState(); } | |||
466 | sub _ChangeState | ||||||
467 | { | ||||||
468 | #** @method private _ChangeState ($state) | ||||||
469 | # This method will change and keep track of the various states that the state machine | ||||||
470 | # transitions to and from. Having this information allows you to return to a previous | ||||||
471 | # state. If you pass nothing in to this method it will restore the previous state. | ||||||
472 | # @param state - optional string (state to change to) | ||||||
473 | #* | ||||||
474 | 1 | 1 | 2 | my $self = shift; | |||
475 | 1 | 1 | my $state = shift; | ||||
476 | 1 | 7 | my $logger = $self->GetLogger($self); | ||||
477 | 1 | 314 | $logger->debug("### Entering _ChangeState ###"); | ||||
478 | |||||||
479 | 1 | 50 | 33 | 64 | if (defined $state && exists $hValidStates->{$state}) | ||
480 | { | ||||||
481 | # If there was a value passed in and it is a valid value lets make it active | ||||||
482 | 1 | 5 | $logger->debug("State passed in: $state"); | ||||
483 | 1 | 50 | 33 | 8 | unless (defined $self->{'_sState'} && $self->{'_sState'} eq $state) | ||
484 | { | ||||||
485 | # Need to push the current state to the array BEFORE we change it and only | ||||||
486 | # if we are not currently at that state | ||||||
487 | 1 | 2 | push (@{$self->{'_sPreviousState'}}, $self->{'_sState'}); | ||||
1 | 3 | ||||||
488 | 1 | 2 | $self->{'_sState'} = $state; | ||||
489 | } | ||||||
490 | } | ||||||
491 | else | ||||||
492 | { | ||||||
493 | # If nothing is passed in, lets set the current state to the preivous state. | ||||||
494 | 0 | 0 | $logger->debug("No state passed in, lets revert to previous state"); | ||||
495 | 0 | 0 | my $previous = pop @{$self->{'_sPreviousState'}}; | ||||
0 | 0 | ||||||
496 | 0 | 0 | 0 | if (defined $previous) | |||
497 | { | ||||||
498 | 0 | 0 | $logger->debug("Previous state was $previous"); | ||||
499 | } | ||||||
500 | else | ||||||
501 | { | ||||||
502 | 0 | 0 | $logger->error("There is no previous state! Setting to NORMAL"); | ||||
503 | 0 | 0 | $previous = 'NORMAL'; | ||||
504 | } | ||||||
505 | 0 | 0 | $self->{'_sState'} = $previous; | ||||
506 | } | ||||||
507 | } | ||||||
508 | |||||||
509 | sub _PrintFilenameBlock | ||||||
510 | { | ||||||
511 | #** @method private _PrintFilenameBlock () | ||||||
512 | # This method will print the filename section in appropriate doxygen syntax | ||||||
513 | #* | ||||||
514 | 0 | 0 | 0 | my $self = shift; | |||
515 | 0 | 0 | my $logger = $self->GetLogger($self); | ||||
516 | 0 | 0 | $logger->debug("### Entering _PrintFilenameBlock ###"); | ||||
517 | |||||||
518 | 0 | 0 | 0 | if (defined $self->{'_hData'}->{'filename'}->{'fullpath'}) | |||
519 | { | ||||||
520 | 0 | 0 | print "/** \@file $self->{'_hData'}->{'filename'}->{'fullpath'}\n"; | ||||
521 | 0 | 0 | 0 | if (defined $self->{'_hData'}->{'filename'}->{'details'}) { print "$self->{'_hData'}->{'filename'}->{'details'}\n"; } | |||
0 | 0 | ||||||
522 | 0 | 0 | 0 | if (defined $self->{'_hData'}->{'filename'}->{'version'}) { print "\@version $self->{'_hData'}->{'filename'}->{'version'}\n"; } | |||
0 | 0 | ||||||
523 | 0 | 0 | print "*/\n"; | ||||
524 | } | ||||||
525 | } | ||||||
526 | |||||||
527 | sub _PrintIncludesBlock | ||||||
528 | { | ||||||
529 | #** @method private _PrintIncludesBlock () | ||||||
530 | # This method will print the various extra modules that are used | ||||||
531 | #* | ||||||
532 | 0 | 0 | 0 | my $self = shift; | |||
533 | 0 | 0 | my $logger = $self->GetLogger($self); | ||||
534 | 0 | 0 | $logger->debug("### Entering _PrintIncludeBlock ###"); | ||||
535 | |||||||
536 | 0 | 0 | foreach my $include (@{$self->{'_hData'}->{'includes'}}) | ||||
0 | 0 | ||||||
537 | { | ||||||
538 | 0 | 0 | print "\#include \"$include.pm\"\n"; | ||||
539 | } | ||||||
540 | 0 | 0 | print "\n"; | ||||
541 | } | ||||||
542 | |||||||
543 | sub _PrintClassBlock | ||||||
544 | { | ||||||
545 | #** @method private _PrintClassBlock ($sFullClass) | ||||||
546 | # This method will print the class/package block in appropriate doxygen syntax | ||||||
547 | # @param sFullClass - required string (full name of the class) | ||||||
548 | #* | ||||||
549 | 0 | 0 | 0 | my $self = shift; | |||
550 | 0 | 0 | my $sFullClass = shift; | ||||
551 | 0 | 0 | my $logger = $self->GetLogger($self); | ||||
552 | 0 | 0 | $logger->debug("### Entering _PrintClassBlock ###"); | ||||
553 | |||||||
554 | # We need to reset the $1 / $2 match for perl scripts without package classes. | ||||||
555 | # so lets do it here just to be save. Yes this is an expensive way of doing it | ||||||
556 | # but it works. | ||||||
557 | 0 | 0 | $sFullClass =~ /./; | ||||
558 | 0 | 0 | $sFullClass =~ /(.*)\:\:(\w+)$/; | ||||
559 | 0 | 0 | my $parent = $1; | ||||
560 | 0 | 0 | 0 | my $class = $2 || $sFullClass; | |||
561 | |||||||
562 | 0 | 0 | print "/** \@class $sFullClass\n"; | ||||
563 | |||||||
564 | 0 | 0 | my $classDef = $self->{'_hData'}->{'class'}->{$sFullClass}; | ||||
565 | |||||||
566 | 0 | 0 | my $details = $self->{'_hData'}->{'class'}->{$sFullClass}->{'details'}; | ||||
567 | 0 | 0 | 0 | if (defined $details) { print "$details\n"; } | |||
0 | 0 | ||||||
568 | |||||||
569 | 0 | 0 | my $comments = $self->{'_hData'}->{'class'}->{$sFullClass}->{'comments'}; | ||||
570 | 0 | 0 | 0 | if (defined $comments) { print "$comments\n"; } | |||
0 | 0 | ||||||
571 | |||||||
572 | 0 | 0 | print "\@nosubgrouping */\n"; | ||||
573 | |||||||
574 | #if (defined $parent) { print "class $sFullClass : public $parent { \n"; } | ||||||
575 | #else { print "class $sFullClass { \n"; } | ||||||
576 | 0 | 0 | 0 | print "namespace $parent {\n" if ($parent); | |||
577 | 0 | 0 | print "class $class"; | ||||
578 | 0 | 0 | 0 | if (@{$classDef->{inherits}}) | |||
0 | 0 | ||||||
579 | { | ||||||
580 | 0 | 0 | my $count = 0; | ||||
581 | 0 | 0 | foreach my $inherit (@{$classDef->{inherits}}) | ||||
0 | 0 | ||||||
582 | { | ||||||
583 | 0 | 0 | 0 | print(($count++ == 0 ? ": " : ", ")." public ::".$inherit); | |||
584 | } | ||||||
585 | } | ||||||
586 | 0 | 0 | print "\n{\n"; | ||||
587 | 0 | 0 | print "public:\n"; | ||||
588 | } | ||||||
589 | |||||||
590 | sub _PrintMethodBlock | ||||||
591 | { | ||||||
592 | #** @method private _PrintMethodBlock ($class, $methodDef) | ||||||
593 | # This method will print the various subroutines/functions/methods in apprporiate doxygen syntax | ||||||
594 | # @param class - required string (name of the class) | ||||||
595 | # @param state - required string (current state) | ||||||
596 | # @param type - required string (type) | ||||||
597 | # @param method - required string (name of method) | ||||||
598 | #* | ||||||
599 | 0 | 0 | 0 | my $self = shift; | |||
600 | 0 | 0 | my $class = shift; | ||||
601 | 0 | 0 | my $method = shift; | ||||
602 | |||||||
603 | 0 | 0 | my $methodDef = $self->{'_hData'}->{'class'}->{$class}->{'subroutines'}->{$method}; | ||||
604 | |||||||
605 | 0 | 0 | my $state = $methodDef->{state}; | ||||
606 | 0 | 0 | my $type = $methodDef->{type}; | ||||
607 | |||||||
608 | 0 | 0 | my $logger = $self->GetLogger($self); | ||||
609 | 0 | 0 | $logger->debug("### Entering _PrintMethodBlock ###"); | ||||
610 | |||||||
611 | 0 | 0 | 0 | my $returntype = $methodDef->{'returntype'} || $type; | |||
612 | 0 | 0 | 0 | my $parameters = $methodDef->{'parameters'} || ""; | |||
613 | |||||||
614 | 0 | 0 | print "/** \@fn $state $returntype $method\($parameters\)\n"; | ||||
615 | |||||||
616 | 0 | 0 | my $details = $methodDef->{'details'}; | ||||
617 | 0 | 0 | 0 | if (defined $details) { print "$details\n"; } | |||
0 | 0 | ||||||
618 | 0 | 0 | else { print "Undocumented Method\n"; } | ||||
619 | |||||||
620 | 0 | 0 | my $comments = $methodDef->{'comments'}; | ||||
621 | 0 | 0 | 0 | if (defined $comments) { print "$comments\n"; } | |||
0 | 0 | ||||||
622 | |||||||
623 | # Print collapsible source code block | ||||||
624 | 0 | 0 | print "\@htmlonly\n"; | ||||
625 | 0 | 0 | print " \n"; |
||||
626 | 0 | 0 | print "\tCode:\n"; | ||||
627 | 0 | 0 | print "\n"; | ||||
628 | 0 | 0 | print " click to view \n"; |
||||
629 | 0 | 0 | print " | ||||
630 | 0 | 0 | print "\@endhtmlonly\n"; | ||||
631 | |||||||
632 | 0 | 0 | print "\@code\n"; | ||||
633 | 0 | 0 | print "\# Number of lines of code in $method: $methodDef->{'length'}\n"; | ||||
634 | 0 | 0 | print "$methodDef->{'code'}\n"; | ||||
635 | 0 | 0 | print "\@endcode \@htmlonly\n"; | ||||
636 | 0 | 0 | print "\n"; | ||||
637 | 0 | 0 | print "\@endhtmlonly */\n"; | ||||
638 | |||||||
639 | 0 | 0 | print "$state $returntype $method\($parameters\)\;\n"; | ||||
640 | } | ||||||
641 | |||||||
642 | sub _ProcessPerlMethod | ||||||
643 | { | ||||||
644 | #** @method private _ProcessPerlMethod ($line) | ||||||
645 | # This method will process the contents of a subroutine/function/method and try to figure out | ||||||
646 | # the name and wether or not it is a private or public method. The private or public status, | ||||||
647 | # if not defined in a doxygen comment block will be determined based on the file name. As with | ||||||
648 | # C and other languages, an "_" should be the first character for all private functions/methods. | ||||||
649 | # @param line - required string (full line of code) | ||||||
650 | #* | ||||||
651 | 0 | 0 | 0 | my $self = shift; | |||
652 | 0 | 0 | my $line = shift; | ||||
653 | 0 | 0 | my $logger = $self->GetLogger($self); | ||||
654 | 0 | 0 | $logger->debug("### Entering _ProcessPerlMethod ###"); | ||||
655 | |||||||
656 | 0 | 0 | my $sClassName = $self->{'_sCurrentClass'}; | ||||
657 | |||||||
658 | 0 | 0 | 0 | if ($line =~ /^\s*sub\s+(.*)/) | |||
659 | { | ||||||
660 | # We should keep track of the order in which the methods were written in the code so we can print | ||||||
661 | # them out in the same order | ||||||
662 | 0 | 0 | my $sName = $1; | ||||
663 | # If they have declared the subrountine with a brace on the same line, lets remove it | ||||||
664 | 0 | 0 | $sName =~ s/\{.*\}?//; | ||||
665 | # Remove any leading or trailing whitespace from the name, just to be safe | ||||||
666 | 0 | 0 | $sName =~ s/\s//g; | ||||
667 | 0 | 0 | $logger->debug("Method Name: $sName"); | ||||
668 | |||||||
669 | 0 | 0 | push (@{$self->{'_hData'}->{'class'}->{$sClassName}->{'subroutineorder'}}, $sName); | ||||
0 | 0 | ||||||
670 | 0 | 0 | $self->{'_sCurrentMethodName'} = $sName; | ||||
671 | } | ||||||
672 | 0 | 0 | my $sMethodName = $self->{'_sCurrentMethodName'}; | ||||
673 | |||||||
674 | # Lets find out if this is a public or private method/function based on a naming standard | ||||||
675 | 0 | 0 | 0 | if ($sMethodName =~ /^_/) { $self->{'_sCurrentMethodState'} = 'private'; } | |||
0 | 0 | ||||||
676 | 0 | 0 | else { $self->{'_sCurrentMethodState'} = 'public'; } | ||||
677 | |||||||
678 | 0 | 0 | my $sMethodState = $self->{'_sCurrentMethodState'}; | ||||
679 | 0 | 0 | $logger->debug("Method State: $sMethodState"); | ||||
680 | |||||||
681 | # We need to count the number of open and close braces so we can see if we are still in a subroutine or not | ||||||
682 | # but we need to becareful so that we do not count braces in comments and braces that are in match patters /\{/ | ||||||
683 | # If there are more open then closed, then we are still in a subroutine | ||||||
684 | 0 | 0 | my $cleanline = $line; | ||||
685 | 0 | 0 | $logger->debug("Cleanline: $cleanline"); | ||||
686 | |||||||
687 | # Remove any comments even those inline with code but not if the hash mark "#" is in a pattern match | ||||||
688 | # unless ($cleanline =~ /=~/) { $cleanline =~ s/#.*$//; } | ||||||
689 | # Patch from Stefan Tauner to address hash marks showing up at the last element of an array, $#array | ||||||
690 | 0 | 0 | 0 | unless ($cleanline =~ /=~/) { $cleanline =~ s/([^\$])#.*$/$1/; } | |||
0 | 0 | ||||||
691 | 0 | 0 | $logger->debug("Cleanline: $cleanline"); | ||||
692 | # Need to remove braces from counting when they are in a pattern match but not when they are supposed to be | ||||||
693 | # there as in the second use case listed below. Below the use cases is some ideas on how to do this. | ||||||
694 | # use case: $a =~ /\{/ | ||||||
695 | # use case: if (/\{/) { foo; } | ||||||
696 | # use case: unless ($cleanline =~ /=~/) { $cleanline =~ s/#.*$//; } | ||||||
697 | 0 | 0 | $cleanline =~ s#/.*?/##g; | ||||
698 | 0 | 0 | $logger->debug("Cleanline: $cleanline"); | ||||
699 | # Remove any braces found in a print statement lile: | ||||||
700 | # use case: print "some foo { bar somethingelse"; | ||||||
701 | # use case: print "$self->{'_hData'}->{'filename'}->{'details'}\n"; | ||||||
702 | 0 | 0 | 0 | if ($cleanline =~ /(.*?print\s*)(.*?);(.*)/) | |||
703 | { | ||||||
704 | 0 | 0 | my $sLineData1 = $1; | ||||
705 | 0 | 0 | my $sLineData2 = $2; | ||||
706 | 0 | 0 | my $sLineData3 = $3; | ||||
707 | 0 | 0 | $sLineData2 =~ s#[{}]##g; | ||||
708 | 0 | 0 | $cleanline = $sLineData1 . $sLineData2. $sLineData3; | ||||
709 | } | ||||||
710 | #$cleanline =~ s/(print\s*\".*){(.*\")/$1$2/g; | ||||||
711 | 0 | 0 | $logger->debug("Cleanline: $cleanline"); | ||||
712 | |||||||
713 | 0 | 0 | $self->{'_iOpenBrace'} += @{[$cleanline =~ /\{/g]}; | ||||
0 | 0 | ||||||
714 | 0 | 0 | $self->{'_iCloseBrace'} += @{[$cleanline =~ /\}/g]}; | ||||
0 | 0 | ||||||
715 | 0 | 0 | $logger->debug("Open Brace Number: $self->{'_iOpenBrace'}"); | ||||
716 | 0 | 0 | $logger->debug("Close Brace Number: $self->{'_iCloseBrace'}"); | ||||
717 | |||||||
718 | |||||||
719 | # Use Case 1: sub foo { return; } | ||||||
720 | # Use Case 2: sub foo {\n} | ||||||
721 | # Use Case 3: sub foo \n {\n } | ||||||
722 | |||||||
723 | 0 | 0 | 0 | 0 | if ($self->{'_iOpenBrace'} > $self->{'_iCloseBrace'}) | ||
0 | |||||||
724 | { | ||||||
725 | # Use Case 2, still in subroutine | ||||||
726 | 0 | 0 | $logger->debug("We are still in the subroutine"); | ||||
727 | } | ||||||
728 | elsif ($self->{'_iOpenBrace'} > 0 && $self->{'_iOpenBrace'} == $self->{'_iCloseBrace'}) | ||||||
729 | { | ||||||
730 | # Use Case 1, we are leaving a subroutine | ||||||
731 | 0 | 0 | $logger->debug("We are leaving the subroutine"); | ||||
732 | 0 | 0 | $self->_ChangeState('NORMAL'); | ||||
733 | 0 | 0 | $self->RESETSUB(); | ||||
734 | } | ||||||
735 | else | ||||||
736 | { | ||||||
737 | # Use Case 3, still in subroutine | ||||||
738 | 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"); | ||||
739 | } | ||||||
740 | |||||||
741 | # Doxygen makes use of the @ symbol and treats it as a special reserved character. This is a problem for perl | ||||||
742 | # and especailly when we are documenting our own Doxygen code we have print statements that include things like @endcode | ||||||
743 | # as is found in _PrintMethodBlock(). Lets convert those @ to @amp; | ||||||
744 | 0 | 0 | $line =~ s/\@endcode/\&\#64\;endcode/g; | ||||
745 | |||||||
746 | # Record the current line for code output | ||||||
747 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'code'} .= $line; | ||||
748 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'length'}++; | ||||
749 | |||||||
750 | # Only set these values if they were not already set by a comment block outside the subroutine | ||||||
751 | # This is for public/private | ||||||
752 | 0 | 0 | 0 | unless (defined $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'state'}) | |||
753 | { | ||||||
754 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'state'} = $sMethodState; | ||||
755 | } | ||||||
756 | # This is for function/method | ||||||
757 | 0 | 0 | 0 | unless (defined $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'type'}) | |||
758 | { | ||||||
759 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'type'} = "method"; | ||||
760 | } | ||||||
761 | } | ||||||
762 | |||||||
763 | sub _ProcessPodCommentBlock | ||||||
764 | { | ||||||
765 | #** @method private _ProcessPodCommentBlock () | ||||||
766 | # This method will process an entire POD block in one pass, after it has all been gathered by the state machine. | ||||||
767 | #* | ||||||
768 | 0 | 0 | 0 | my $self = shift; | |||
769 | 0 | 0 | my $logger = $self->GetLogger($self); | ||||
770 | 0 | 0 | $logger->debug("### Entering _ProcessPodCommentBlock ###"); | ||||
771 | |||||||
772 | 0 | 0 | my $sClassName = $self->{'_sCurrentClass'}; | ||||
773 | 0 | 0 | my @aBlock = @{$self->{'_aPodBlock'}}; | ||||
0 | 0 | ||||||
774 | |||||||
775 | # 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 | ||||||
776 | # sure it is all clean and ready for the next comment block | ||||||
777 | 0 | 0 | $self->RESETPOD(); | ||||
778 | |||||||
779 | 0 | 0 | my $sPodRawText; | ||||
780 | 0 | 0 | foreach (@aBlock) | ||||
781 | { | ||||||
782 | # If we find any Doxygen special characters in the POD, lets escape them | ||||||
783 | 0 | 0 | s/(\@|\\|\%|#)/\\$1/g; | ||||
784 | 0 | 0 | $sPodRawText .= $_; | ||||
785 | } | ||||||
786 | |||||||
787 | 0 | 0 | my $parser = new Pod::POM(); | ||||
788 | 0 | 0 | my $pom = $parser->parse_text($sPodRawText); | ||||
789 | 0 | 0 | my $sPodParsedText = Doxygen::Filter::Perl::POD->print($pom); | ||||
790 | |||||||
791 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'comments'} .= $sPodParsedText; | ||||
792 | } | ||||||
793 | |||||||
794 | |||||||
795 | sub _ProcessDoxygenCommentBlock | ||||||
796 | { | ||||||
797 | #** @method private _ProcessDoxygenCommentBlock () | ||||||
798 | # This method will process an entire comment block in one pass, after it has all been gathered by the state machine | ||||||
799 | #* | ||||||
800 | 0 | 0 | 0 | my $self = shift; | |||
801 | 0 | 0 | my $logger = $self->GetLogger($self); | ||||
802 | 0 | 0 | $logger->debug("### Entering _ProcessDoxygenCommentBlock ###"); | ||||
803 | |||||||
804 | 0 | 0 | my @aBlock = @{$self->{'_aDoxygenBlock'}}; | ||||
0 | 0 | ||||||
805 | |||||||
806 | # 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 | ||||||
807 | # sure it is all clean and ready for the next comment block | ||||||
808 | 0 | 0 | $self->RESETDOXY(); | ||||
809 | |||||||
810 | 0 | 0 | my $sClassName = $self->{'_sCurrentClass'}; | ||||
811 | 0 | 0 | my $sSubState = ''; | ||||
812 | 0 | 0 | $logger->debug("We are currently in class $sClassName"); | ||||
813 | |||||||
814 | # Lets grab the command line and put it in a variable for easier use | ||||||
815 | 0 | 0 | my $sCommandLine = $aBlock[0]; | ||||
816 | 0 | 0 | $logger->debug("The command line for this doxygen comment is $sCommandLine"); | ||||
817 | |||||||
818 | 0 | 0 | $sCommandLine =~ /^\s*#\*\*\s+\@([\w:]+)\s+(.*)/; | ||||
819 | 0 | 0 | my $sCommand = lc($1); | ||||
820 | 0 | 0 | my $sOptions = $2; | ||||
821 | 0 | 0 | $logger->debug("Command: $sCommand"); | ||||
822 | 0 | 0 | $logger->debug("Options: $sOptions"); | ||||
823 | |||||||
824 | # If the user entered @fn instead of @function, lets change it | ||||||
825 | 0 | 0 | 0 | if ($sCommand eq "fn") { $sCommand = "function"; } | |||
0 | 0 | ||||||
826 | |||||||
827 | # Lets find out what doxygen sub state we should be in | ||||||
828 | 0 | 0 | 0 | if ($sCommand eq 'file') { $sSubState = 'DOXYFILE'; } | |||
0 | 0 | 0 | |||||
0 | |||||||
0 | |||||||
0 | |||||||
0 | |||||||
0 | |||||||
829 | 0 | 0 | elsif ($sCommand eq 'class') { $sSubState = 'DOXYCLASS'; } | ||||
830 | 0 | 0 | elsif ($sCommand eq 'package') { $sSubState = 'DOXYCLASS'; } | ||||
831 | 0 | 0 | elsif ($sCommand eq 'function') { $sSubState = 'DOXYFUNCTION'; } | ||||
832 | 0 | 0 | elsif ($sCommand eq 'method') { $sSubState = 'DOXYMETHOD'; } | ||||
833 | 0 | 0 | elsif ($sCommand eq 'attr') { $sSubState = 'DOXYATTR'; } | ||||
834 | 0 | 0 | elsif ($sCommand eq 'var') { $sSubState = 'DOXYATTR'; } | ||||
835 | 0 | 0 | else { $sSubState = 'DOXYCOMMENT'; } | ||||
836 | 0 | 0 | $logger->debug("Substate is now $sSubState"); | ||||
837 | |||||||
838 | 0 | 0 | 0 | 0 | if ($sSubState eq 'DOXYFILE' ) | ||
0 | |||||||
0 | |||||||
0 | |||||||
0 | |||||||
839 | { | ||||||
840 | 0 | 0 | $logger->debug("Processing a Doxygen file object"); | ||||
841 | # We need to remove the command line from this block | ||||||
842 | 0 | 0 | shift @aBlock; | ||||
843 | 0 | 0 | $self->{'_hData'}->{'filename'}->{'details'} = $self->_RemovePerlCommentFlags(\@aBlock); | ||||
844 | } | ||||||
845 | elsif ($sSubState eq 'DOXYCLASS') | ||||||
846 | { | ||||||
847 | 0 | 0 | $logger->debug("Processing a Doxygen class object"); | ||||
848 | #my $sClassName = $sOptions; | ||||||
849 | 0 | 0 | 0 | my $sClassName = $sOptions || $sClassName; | |||
850 | 0 | 0 | my $classDef = $self->_SwitchClass($sClassName); | ||||
851 | # We need to remove the command line from this block | ||||||
852 | 0 | 0 | shift @aBlock; | ||||
853 | #$self->{'_hData'}->{'class'}->{$sClassName}->{'details'} = $self->_RemovePerlCommentFlags(\@aBlock); | ||||||
854 | 0 | 0 | $classDef->{'details'} = $self->_RemovePerlCommentFlags(\@aBlock); | ||||
855 | } | ||||||
856 | elsif ($sSubState eq 'DOXYCOMMENT') | ||||||
857 | { | ||||||
858 | 0 | 0 | $logger->debug("Processing a Doxygen class object"); | ||||
859 | # For extra comment blocks we need to add the command and option line back to the front of the array | ||||||
860 | 0 | 0 | my $sMethodName = $self->{'_sCurrentMethodName'}; | ||||
861 | 0 | 0 | 0 | if (defined $sMethodName) | |||
862 | { | ||||||
863 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'comments'} .= "\n"; | ||||
864 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'comments'} .= $self->_RemovePerlCommentFlags(\@aBlock); | ||||
865 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'comments'} .= "\n"; | ||||
866 | } | ||||||
867 | else | ||||||
868 | { | ||||||
869 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'comments'} .= "\n"; | ||||
870 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'comments'} .= $self->_RemovePerlCommentFlags(\@aBlock); | ||||
871 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'comments'} .= "\n"; | ||||
872 | } | ||||||
873 | } | ||||||
874 | elsif ($sSubState eq 'DOXYATTR') | ||||||
875 | { | ||||||
876 | # Process the doxygen header first then loop through the rest of the comments | ||||||
877 | #my ($sState, $sAttrName, $sComments) = ($sOptions =~ /(?:(public|private)\s+)?([\$@%\*][\w:]+)\s+(.*)/); | ||||||
878 | 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+(.*)/); | ||||
879 | 0 | 0 | 0 | if (defined $sAttrName) | |||
880 | { | ||||||
881 | 0 | 0 | 0 | my $attrDef = $self->{'_hData'}->{'class'}->{$sClassName}->{'attributes'}->{$sAttrName} ||= {}; | |||
882 | 0 | 0 | 0 | if ($typeName) | |||
883 | { | ||||||
884 | 0 | 0 | $attrDef->{'type'} = $typeName; | ||||
885 | } | ||||||
886 | else | ||||||
887 | { | ||||||
888 | 0 | 0 | $attrDef->{'type'} = $self->_ConvertTypeCode($typeCode); | ||||
889 | } | ||||||
890 | 0 | 0 | 0 | if (defined $sState) | |||
891 | { | ||||||
892 | 0 | 0 | $attrDef->{'state'} = $sState; | ||||
893 | } | ||||||
894 | 0 | 0 | 0 | if (defined $sComments) | |||
895 | { | ||||||
896 | 0 | 0 | $attrDef->{'comments'} = $sComments; | ||||
897 | } | ||||||
898 | 0 | 0 | 0 | if (defined $modifiers) | |||
899 | { | ||||||
900 | 0 | 0 | $attrDef->{'modifiers'} = $modifiers; | ||||
901 | } | ||||||
902 | ## We need to remove the command line from this block | ||||||
903 | 0 | 0 | shift @aBlock; | ||||
904 | 0 | 0 | $attrDef->{'details'} = $self->_RemovePerlCommentFlags(\@aBlock); | ||||
905 | 0 | 0 | push(@{$self->GetCurrentClass()->{attributeorder}}, $sAttrName); | ||||
0 | 0 | ||||||
906 | } | ||||||
907 | else | ||||||
908 | { | ||||||
909 | 0 | 0 | $self->ReportError("invalid syntax for attribute: $sOptions\n"); | ||||
910 | } | ||||||
911 | } # End DOXYATTR | ||||||
912 | elsif ($sSubState eq 'DOXYFUNCTION' || $sSubState eq 'DOXYMETHOD') | ||||||
913 | { | ||||||
914 | # Process the doxygen header first then loop through the rest of the comments | ||||||
915 | 0 | 0 | $sOptions =~ /^(.*?)\s*\(\s*(.*?)\s*\)/; | ||||
916 | 0 | 0 | $sOptions = $1; | ||||
917 | 0 | 0 | my $sParameters = $2; | ||||
918 | |||||||
919 | 0 | 0 | my @aOptions; | ||||
920 | my $state; | ||||||
921 | 0 | 0 | my $sMethodName; | ||||
922 | |||||||
923 | 0 | 0 | 0 | if (defined $sOptions) | |||
924 | { | ||||||
925 | 0 | 0 | @aOptions = split(/\s+/, $sOptions); | ||||
926 | # State = Public/Private | ||||||
927 | 0 | 0 | 0 | 0 | if ($aOptions[0] eq "public" || $aOptions[0] eq "private" || $aOptions[0] eq "protected") | ||
0 | |||||||
928 | { | ||||||
929 | 0 | 0 | $state = shift @aOptions; | ||||
930 | } | ||||||
931 | 0 | 0 | $sMethodName = pop(@aOptions); | ||||
932 | } | ||||||
933 | |||||||
934 | 0 | 0 | 0 | 0 | if ($sSubState eq "DOXYFUNCTION" && !grep(/^static$/, @aOptions)) | ||
935 | { | ||||||
936 | 0 | 0 | unshift(@aOptions, "static"); | ||||
937 | } | ||||||
938 | |||||||
939 | 0 | 0 | 0 | unless (defined $sMethodName) | |||
940 | { | ||||||
941 | # If we are already in a subroutine and a user uses sloppy documentation and only does | ||||||
942 | # #**@method in side the subroutine, then lets pull the current method name from the object. | ||||||
943 | # If there is no method defined there, we should die. | ||||||
944 | 0 | 0 | 0 | if (defined $self->{'_sCurrentMethodName'}) { $sMethodName = $self->{'_sCurrentMethodName'}; } | |||
0 | 0 | ||||||
945 | 0 | 0 | else { die "Missing method name in $sCommand syntax"; } | ||||
946 | } | ||||||
947 | |||||||
948 | # If we are not yet in a subroutine, lets keep track that we are now processing a subroutine and its name | ||||||
949 | 0 | 0 | 0 | unless (defined $self->{'_sCurrentMethodName'}) { $self->{'_sCurrentMethodName'} = $sMethodName; } | |||
0 | 0 | ||||||
950 | |||||||
951 | 0 | 0 | 0 | if (defined $sParameters) { $sParameters = $self->_ConvertParameters($sParameters); } | |||
0 | 0 | ||||||
952 | |||||||
953 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'returntype'} = join(" ", @aOptions); | ||||
954 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'type'} = $sCommand; | ||||
955 | 0 | 0 | 0 | if (defined $state) | |||
956 | { | ||||||
957 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'state'} = $state; | ||||
958 | } | ||||||
959 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'parameters'} = $sParameters; | ||||
960 | # We need to remove the command line from this block | ||||||
961 | 0 | 0 | shift @aBlock; | ||||
962 | 0 | 0 | $self->{'_hData'}->{'class'}->{$sClassName}->{'subroutines'}->{$sMethodName}->{'details'} = $self->_RemovePerlCommentFlags(\@aBlock); | ||||
963 | |||||||
964 | } # End DOXYFUNCTION || DOXYMETHOD | ||||||
965 | } | ||||||
966 | |||||||
967 | sub _RemovePerlCommentFlags | ||||||
968 | { | ||||||
969 | #** @method private _RemovePerlCommentFlags ($aBlock) | ||||||
970 | # This method will remove all of the comment marks "#" for our output to Doxygen. If the line is | ||||||
971 | # flagged for verbatim then lets not do anything. | ||||||
972 | # @param aBlock - required array_ref (doxygen comment as an array of code lines) | ||||||
973 | # @retval sBlockDetails - string (doxygen comments in one long string) | ||||||
974 | #* | ||||||
975 | 0 | 0 | 0 | my $self = shift; | |||
976 | 0 | 0 | my $aBlock = shift; | ||||
977 | 0 | 0 | my $logger = $self->GetLogger($self); | ||||
978 | 0 | 0 | $logger->debug("### Entering _RemovePerlCommentFlags ###"); | ||||
979 | |||||||
980 | 0 | 0 | my $sBlockDetails = ""; | ||||
981 | 0 | 0 | my $iInVerbatimBlock = 0; | ||||
982 | 0 | 0 | foreach my $line (@$aBlock) | ||||
983 | { | ||||||
984 | # Lets check for a verbatim command option like '# @verbatim' | ||||||
985 | 0 | 0 | 0 | if ($line =~ /^\s*#\s*\@verbatim/) | |||
0 | |||||||
986 | { | ||||||
987 | 0 | 0 | $logger->debug("Found verbatim command"); | ||||
988 | # We need to remove the comment marker from the '# @verbaim' line now since it will not be caught later | ||||||
989 | 0 | 0 | $line =~ s/^\s*#\s*/ /; | ||||
990 | 0 | 0 | $iInVerbatimBlock = 1; | ||||
991 | } | ||||||
992 | elsif ($line =~ /^\s*#\s*\@endverbatim/) | ||||||
993 | { | ||||||
994 | 0 | 0 | $logger->debug("Found endverbatim command"); | ||||
995 | 0 | 0 | $iInVerbatimBlock = 0; | ||||
996 | } | ||||||
997 | # Lets remove any doxygen command initiator | ||||||
998 | 0 | 0 | $line =~ s/^\s*#\*\*\s*//; | ||||
999 | # Lets remove any doxygen command terminators | ||||||
1000 | 0 | 0 | $line =~ s/^\s*#\*\s*//; | ||||
1001 | # Lets remove all of the Perl comment markers so long as we are not in a verbatim block | ||||||
1002 | # if ($iInVerbatimBlock == 0) { $line =~ s/^\s*#+//; } | ||||||
1003 | # Patch from Sebastian Rose to address spacing and indentation in code examples | ||||||
1004 | 0 | 0 | 0 | if ($iInVerbatimBlock == 0) { $line =~ s/^\s*#\s?//; } | |||
0 | 0 | ||||||
1005 | 0 | 0 | $logger->debug("code: $line"); | ||||
1006 | # Patch from Mihai MOJE to address method comments all on the same line. | ||||||
1007 | 0 | 0 | $sBlockDetails .= $line . " "; |
||||
1008 | #$sBlockDetails .= $line; | ||||||
1009 | } | ||||||
1010 | 0 | 0 | $sBlockDetails =~ s/^([ \t]*\n)+//s; | ||||
1011 | 0 | 0 | chomp($sBlockDetails); | ||||
1012 | 0 | 0 | 0 | if ($sBlockDetails) | |||
1013 | { | ||||||
1014 | 0 | 0 | $sBlockDetails =~ s/^/ \*/gm; | ||||
1015 | 0 | 0 | $sBlockDetails .= "\n"; | ||||
1016 | } | ||||||
1017 | 0 | 0 | return $sBlockDetails; | ||||
1018 | } | ||||||
1019 | |||||||
1020 | sub _ConvertToOfficialDoxygenSyntax | ||||||
1021 | { | ||||||
1022 | #** @method private _ConvertToOfficialDoxygenSyntax ($line) | ||||||
1023 | # This method will check the current line for various unsupported doxygen comment blocks and convert them | ||||||
1024 | # to the type we support, '#** @command'. The reason for this is so that we do not need to add them in | ||||||
1025 | # every if statement throughout the code. | ||||||
1026 | # @param line - required string (line of code) | ||||||
1027 | # @retval line - string (line of code) | ||||||
1028 | #* | ||||||
1029 | 7 | 7 | 7 | my $self = shift; | |||
1030 | 7 | 17 | my $line = shift; | ||||
1031 | 7 | 13 | my $logger = $self->GetLogger($self); | ||||
1032 | 7 | 328 | $logger->debug("### Entering _ConvertToOfficialDoxygenSyntax ###"); | ||||
1033 | |||||||
1034 | # This will match "## @command" and convert it to "#** @command" | ||||||
1035 | 7 | 50 | 39 | if ($line =~ /^\s*##\s+\@/) { $line =~ s/^(\s*)##(\s+\@)/$1#\*\*$2/; } | |||
0 | 0 | ||||||
1036 | 7 | 10 | return $line; | ||||
1037 | } | ||||||
1038 | |||||||
1039 | sub _ConvertTypeCode | ||||||
1040 | { | ||||||
1041 | #** @method private _ConvertTypeCode($code) | ||||||
1042 | # This method will change the $, @, and %, etc to written names so that Doxygen does not have a problem with them | ||||||
1043 | # @param code | ||||||
1044 | # required prefix of variable | ||||||
1045 | #* | ||||||
1046 | 0 | 0 | my $self = shift; | ||||
1047 | 0 | my $code = shift; | |||||
1048 | 0 | my $logger = $self->GetLogger($self); | |||||
1049 | 0 | $logger->debug("### Entering _ConvertParameters ###"); | |||||
1050 | |||||||
1051 | # Lets clean up the parameters list so that it will work with Doxygen | ||||||
1052 | 0 | $code =~ s/\$\$/scalar_ref/g; | |||||
1053 | 0 | $code =~ s/\@\$/array_ref/g; | |||||
1054 | 0 | $code =~ s/\%\$/hash_ref/g; | |||||
1055 | 0 | $code =~ s/\$/scalar/g; | |||||
1056 | 0 | $code =~ s/\@/array/g; | |||||
1057 | 0 | $code =~ s/\%/hash/g; | |||||
1058 | |||||||
1059 | 0 | return $code; | |||||
1060 | } | ||||||
1061 | |||||||
1062 | sub _ConvertParameters | ||||||
1063 | { | ||||||
1064 | #** @method private _ConvertParameters () | ||||||
1065 | # This method will change the $, @, and %, etc to written names so that Doxygen does not have a problem with them | ||||||
1066 | # @param sParameters - required string (variable parameter to change) | ||||||
1067 | #* | ||||||
1068 | 0 | 0 | my $self = shift; | ||||
1069 | 0 | my $sParameters = shift; | |||||
1070 | 0 | my $logger = $self->GetLogger($self); | |||||
1071 | 0 | $logger->debug("### Entering _ConvertParameters ###"); | |||||
1072 | |||||||
1073 | # Lets clean up the parameters list so that it will work with Doxygen | ||||||
1074 | 0 | $sParameters =~ s/\$\$/scalar_ref /g; | |||||
1075 | 0 | $sParameters =~ s/\@\$/array_ref /g; | |||||
1076 | 0 | $sParameters =~ s/\%\$/hash_ref /g; | |||||
1077 | 0 | $sParameters =~ s/\$/scalar /g; | |||||
1078 | 0 | $sParameters =~ s/\@/array /g; | |||||
1079 | 0 | $sParameters =~ s/\%/hash /g; | |||||
1080 | |||||||
1081 | 0 | return $sParameters; | |||||
1082 | } | ||||||
1083 | |||||||
1084 | =head1 NAME | ||||||
1085 | |||||||
1086 | Doxygen::Filter::Perl - A perl code pre-filter for Doxygen | ||||||
1087 | |||||||
1088 | =head1 DESCRIPTION | ||||||
1089 | |||||||
1090 | The Doxygen::Filter::Perl module is designed to provide support for documenting | ||||||
1091 | perl scripts and modules to be used with the Doxygen engine. We plan on | ||||||
1092 | supporting most Doxygen style comments and POD (plain old documentation) style | ||||||
1093 | comments. The Doxgyen style comment blocks for methods/functions can be inside | ||||||
1094 | or outside the method/function. Doxygen::Filter::Perl is hosted at | ||||||
1095 | http://perldoxygen.sourceforge.net/ | ||||||
1096 | |||||||
1097 | =head1 USAGE | ||||||
1098 | |||||||
1099 | Install Doxygen::Filter::Perl via CPAN or from source. If you install from | ||||||
1100 | source then do: | ||||||
1101 | |||||||
1102 | perl Makefile.PL | ||||||
1103 | make | ||||||
1104 | make install | ||||||
1105 | |||||||
1106 | Make sure that the doxygen-filter-perl script was copied from this project into | ||||||
1107 | your path somewhere and that it has RX permissions. Example: | ||||||
1108 | |||||||
1109 | /usr/local/bin/doxygen-filter-perl | ||||||
1110 | |||||||
1111 | Copy over the Doxyfile file from this project into the root directory of your | ||||||
1112 | project so that it is at the same level as your lib directory. This file will | ||||||
1113 | have all of the presets needed for documenting Perl code. You can edit this | ||||||
1114 | file with the doxywizard tool if you so desire or if you need to change the | ||||||
1115 | lib directory location or the output location (the default output is ./doc). | ||||||
1116 | Please see the Doxygen manual for information on how to configure the Doxyfile | ||||||
1117 | via a text editor or with the doxywizard tool. | ||||||
1118 | Example: | ||||||
1119 | |||||||
1120 | /home/jordan/workspace/PerlDoxygen/trunk/Doxyfile | ||||||
1121 | /home/jordan/workspace/PerlDoxygen/trunk/lib/Doxygen/Filter/Perl.pm | ||||||
1122 | |||||||
1123 | Once you have done this you can simply run the following from the root of your | ||||||
1124 | project to document your Perl scripts or methods. Example: | ||||||
1125 | |||||||
1126 | /home/jordan/workspace/PerlDoxygen/trunk/> doxygen Doxyfile | ||||||
1127 | |||||||
1128 | All of your documentation will be in the ./doc/html/ directory inside of your | ||||||
1129 | project root. | ||||||
1130 | |||||||
1131 | =head1 DOXYGEN SUPPORT | ||||||
1132 | |||||||
1133 | The following Doxygen style comment is the preferred block style, though others | ||||||
1134 | are supported and are listed below: | ||||||
1135 | |||||||
1136 | #** | ||||||
1137 | # ........ | ||||||
1138 | #* | ||||||
1139 | |||||||
1140 | You can also start comment blocks with "##" and end comment blocks with a blank | ||||||
1141 | line or real code, this allows you to place comments right next to the | ||||||
1142 | subroutines that they refer to if you wish. A comment block must have | ||||||
1143 | continuous "#" comment markers as a blank line can be used as a termination | ||||||
1144 | mark for the doxygen comment block. | ||||||
1145 | |||||||
1146 | In other languages the Doxygen @fn structural indicator is used to document | ||||||
1147 | subroutines/functions/methods and the parsing engine figures out what is what. | ||||||
1148 | In Perl that is a lot harder to do so I have added a @method and @function | ||||||
1149 | structural indicator so that they can be documented seperatly. | ||||||
1150 | |||||||
1151 | =head2 Supported Structural Indicators | ||||||
1152 | |||||||
1153 | #** @file [filename] | ||||||
1154 | # ........ | ||||||
1155 | #* | ||||||
1156 | |||||||
1157 | #** @class [class name (ex. Doxygen::Filter::Perl)] | ||||||
1158 | # ........ | ||||||
1159 | #* | ||||||
1160 | |||||||
1161 | #** @method or @function [public|protected|private] [method-name] (parameters) | ||||||
1162 | # ........ | ||||||
1163 | #* | ||||||
1164 | |||||||
1165 | #** @attr or @var [public|protected|private] [type] {$%@}[attribute-name] [brief description] | ||||||
1166 | # ........ | ||||||
1167 | #* | ||||||
1168 | |||||||
1169 | #** @section [section-name] [section-title] | ||||||
1170 | # ........ | ||||||
1171 | #* | ||||||
1172 | |||||||
1173 | #** @brief [notes] | ||||||
1174 | # ........ | ||||||
1175 | #* | ||||||
1176 | |||||||
1177 | =head2 Support Style Options and Section Indicators | ||||||
1178 | |||||||
1179 | All doxygen style options and section indicators are supported inside the | ||||||
1180 | structural indicators that we currently support. | ||||||
1181 | |||||||
1182 | =head2 Documenting Subroutines/Functions/Methods | ||||||
1183 | |||||||
1184 | The Doxygen style comment blocks that describe a function or method can | ||||||
1185 | exist before, after, or inside the subroutine that it is describing. Examples | ||||||
1186 | are listed below. It is also important to note that you can leave the public/private | ||||||
1187 | out and the filter will guess based on the subroutine name. The normal convention | ||||||
1188 | in other languages like C is to have the function/method start with an "_" if it | ||||||
1189 | is private/protected. We do the same thing here even though there is really no | ||||||
1190 | such thing in Perl. The whole reason for this is to help users of the code know | ||||||
1191 | what functions they should call directly and which they should not. The generic | ||||||
1192 | documentation blocks for functions and methods look like: | ||||||
1193 | |||||||
1194 | #** @function [public|protected|private] [return-type] function-name (parameters) | ||||||
1195 | # @brief A brief description of the function | ||||||
1196 | # | ||||||
1197 | # A detailed description of the function | ||||||
1198 | # @params value [required|optional] [details] | ||||||
1199 | # @retval value [details] | ||||||
1200 | # .... | ||||||
1201 | #* | ||||||
1202 | |||||||
1203 | #** @method [public|protected|private] [return-type] method-name (parameters) | ||||||
1204 | # @brief A brief description of the method | ||||||
1205 | # | ||||||
1206 | # A detailed description of the method | ||||||
1207 | # @params value [required|optional] [details] | ||||||
1208 | # @retval value [details] | ||||||
1209 | # .... | ||||||
1210 | #* | ||||||
1211 | |||||||
1212 | The parameters would normally be something like $foo, @bar, or %foobar. I have | ||||||
1213 | also added support for scalar, array, and hash references and those would be | ||||||
1214 | documented as $$foo, @$bar, %$foobar. An example would look this: | ||||||
1215 | |||||||
1216 | #** @method public ProcessDataValues ($$sFile, %$hDataValues) | ||||||
1217 | |||||||
1218 | =head2 Function / Method Example | ||||||
1219 | |||||||
1220 | sub test1 | ||||||
1221 | { | ||||||
1222 | #** @method public test1 ($value) | ||||||
1223 | # .... | ||||||
1224 | #* | ||||||
1225 | } | ||||||
1226 | |||||||
1227 | #** @method public test2 ($value) | ||||||
1228 | # .... | ||||||
1229 | #* | ||||||
1230 | sub test2 | ||||||
1231 | { | ||||||
1232 | |||||||
1233 | } | ||||||
1234 | |||||||
1235 | =head1 DATA STRUCTURE | ||||||
1236 | |||||||
1237 | $self->{'_hData'}->{'filename'}->{'fullpath'} = string | ||||||
1238 | $self->{'_hData'}->{'filename'}->{'shortname'} = string | ||||||
1239 | $self->{'_hData'}->{'filename'}->{'version'} = string | ||||||
1240 | $self->{'_hData'}->{'filename'}->{'details'} = string | ||||||
1241 | $self->{'_hData'}->{'includes'} = array | ||||||
1242 | |||||||
1243 | $self->{'_hData'}->{'class'}->{'classorder'} = array | ||||||
1244 | $self->{'_hData'}->{'class'}->{$class}->{'subroutineorder'} = array | ||||||
1245 | $self->{'_hData'}->{'class'}->{$class}->{'attributeorder'} = array | ||||||
1246 | $self->{'_hData'}->{'class'}->{$class}->{'details'} = string | ||||||
1247 | $self->{'_hData'}->{'class'}->{$class}->{'comments'} = string | ||||||
1248 | |||||||
1249 | $self->{'_hData'}->{'class'}->{$class}->{'subroutines'}->{$method}->{'type'} = string (method / function) | ||||||
1250 | $self->{'_hData'}->{'class'}->{$class}->{'subroutines'}->{$method}->{'returntype'} = string (return type) | ||||||
1251 | $self->{'_hData'}->{'class'}->{$class}->{'subroutines'}->{$method}->{'state'} = string (public / private) | ||||||
1252 | $self->{'_hData'}->{'class'}->{$class}->{'subroutines'}->{$method}->{'parameters'} = string (method / function parameters) | ||||||
1253 | $self->{'_hData'}->{'class'}->{$class}->{'subroutines'}->{$method}->{'code'} = string | ||||||
1254 | $self->{'_hData'}->{'class'}->{$class}->{'subroutines'}->{$method}->{'length'} = integer | ||||||
1255 | $self->{'_hData'}->{'class'}->{$class}->{'subroutines'}->{$method}->{'details'} = string | ||||||
1256 | $self->{'_hData'}->{'class'}->{$class}->{'subroutines'}->{$method}->{'comments'} = string | ||||||
1257 | |||||||
1258 | $self->{'_hData'}->{'class'}->{$class}->{'attributes'}->{$variable}->{'state'} = string (public / private) | ||||||
1259 | $self->{'_hData'}->{'class'}->{$class}->{'attributes'}->{$variable}->{'modifiers'} = string | ||||||
1260 | $self->{'_hData'}->{'class'}->{$class}->{'attributes'}->{$variable}->{'comments'} = string | ||||||
1261 | $self->{'_hData'}->{'class'}->{$class}->{'attributes'}->{$variable}->{'details'} = string | ||||||
1262 | |||||||
1263 | =head1 AUTHOR | ||||||
1264 | |||||||
1265 | Bret Jordan |
||||||
1266 | |||||||
1267 | =head1 LICENSE | ||||||
1268 | |||||||
1269 | Doxygen::Filter::Perl is dual licensed GPLv3 and Commerical. See the LICENSE | ||||||
1270 | file for more details. | ||||||
1271 | |||||||
1272 | =cut | ||||||
1273 | |||||||
1274 | return 1; |