| 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 Code:\n"; |
||||
| 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; |