File Coverage

lib/Doxygen/Filter/Perl.pm
Criterion Covered Total %
statement 95 502 18.9
branch 14 182 7.6
condition 3 60 5.0
subroutine 20 36 55.5
pod 0 11 0.0
total 132 791 16.6


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