File Coverage

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