File Coverage

lib/Doxygen/Filter/Perl.pm
Criterion Covered Total %
statement 96 504 19.0
branch 14 182 7.6
condition 3 60 5.0
subroutine 20 36 55.5
pod 0 11 0.0
total 133 793 16.7


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