DoxyFilter.pm
Go to the documentation of this file.
00001 ############################################################################## 00002 # @file DoxyFilter.pm 00003 # @brief Base class for Doxygen filter implementations. 00004 # 00005 # @note Not to confuse with the Doxygen::Filter::Perl package available on CPAN. 00006 # 00007 # Copyright (c) 2012 University of Pennsylvania. All rights reserved. 00008 # See https://www.cbica.upenn.edu/sbia/software/license.html or COPYING file. 00009 # 00010 # Contact: SBIA Group <sbia-software at uphs.upenn.edu> 00011 ############################################################################## 00012 00013 use 5.8.3; 00014 use strict; 00015 use warnings; 00016 00017 package BASIS::DoxyFilter; 00018 00019 # ============================================================================ 00020 # exports 00021 # ============================================================================ 00022 00023 use Exporter qw(import); 00024 00025 our $VERSION = '1.0.0'; 00026 our @EXPORT_OK = qw(FROM CONDITION ACTION TO CODE LABELS); 00027 our %EXPORT_TAGS = (indices => [qw(FROM CONDITION ACTION TO CODE LABELS)]); 00028 00029 # ============================================================================ 00030 # constants 00031 # ============================================================================ 00032 00033 ## @brief Array indices for transition 4-tuple. 00034 use constant { 00035 FROM => 0, # current state of filter 00036 CONDITION => 1, # condition (regex line must match) for transition 00037 ACTION => 2, # action to perform upon transition 00038 TO => 3 # state to transition to 00039 }; 00040 00041 ## @brief Array indices for output lines. 00042 use constant { 00043 CODE => 0, # line of output code 00044 LABELS => 1 # array of labels associated with this line 00045 }; 00046 00047 # ============================================================================ 00048 # public 00049 # ============================================================================ 00050 00051 # ---------------------------------------------------------------------------- 00052 ## @brief Constructs a Doxygen filter object. 00053 sub new 00054 { 00055 my $class = shift; 00056 my $transitions = shift; 00057 my $doxydoc_begin = shift; 00058 my $doxydoc_line = shift; 00059 my $doxydoc_end = shift; 00060 # default settings 00061 $doxydoc_begin = qr/##+/ unless defined $doxydoc_begin; 00062 $doxydoc_line = qr/##*/ unless defined $doxydoc_line; 00063 $doxydoc_end = qr/[^#]/ unless defined $doxydoc_end; 00064 $transitions = [] unless defined $transitions; 00065 # add default transitions for handling of Doxygen comment blocks 00066 push @$transitions, ['start', qr/^$doxydoc_begin(.*)$/, \&_doxydoc_begin, 'doxydoc']; 00067 push @$transitions, ['doxydoc', qr/^$doxydoc_line(\s*[\@])param\s*(\[\s*in\s*\]|\[\s*out\s*\]|\[\s*in,\s*out\s*\]|\[\s*out,\s*in\s*\])?\s+(\w+)\s+(.*)$/, \&_doxydoc_param, 'doxydoc']; 00068 push @$transitions, ['doxydoc', qr/^$doxydoc_line((\s*[\@])returns?\s+.*)$/, \&_doxydoc_returns, 'doxydoc']; 00069 push @$transitions, ['doxydoc', qr/^$doxydoc_line(.*)$/, \&_doxydoc_comment, 'doxydoc']; 00070 push @$transitions, ['doxydoc', qr/^$doxydoc_end|^$/, \&_doxydoc_end, 'start']; 00071 # last transition is handling all none-blank lines 00072 push @$transitions, ['start', qr/[^\s]+/, \&_noneblank, 'start']; 00073 # initialize object and return it 00074 return bless { 00075 'transitions' => $transitions, # reference to array defining the transitions 00076 'output' => [] # generated output lines 00077 }, $class; 00078 } 00079 00080 # ---------------------------------------------------------------------------- 00081 ## @brief Process input file. 00082 sub process 00083 { 00084 my $self = shift; 00085 my $filename = shift; 00086 my ($line, $next, @match); 00087 00088 $self->{'state'} = 'start'; # initial start state of filter 00089 $self->{'history'} = ['start']; # linear history of visited states 00090 $self->{'reprocess'} = 0; # can be set by actions to request a 00091 # reprocessing of the current line after 00092 # the state has been changed 00093 $self->{'line'} = ''; # current input line 00094 $self->{'lineno'} = 0; # current line number of input 00095 $self->{'params'} = []; # parameters extracted from comment 00096 00097 open FILE, $filename or die "Failed to open file $filename!"; 00098 while ($self->{'reprocess'} == 1 or $self->{'line'} = <FILE>) { 00099 if ($self->{'reprocess'}) { 00100 $self->{'reprocess'} = 0; 00101 } else { 00102 chomp $self->{'line'}; 00103 $self->{'lineno'} += 1; 00104 } 00105 foreach my $transition (@{$self->{'transitions'}}) { 00106 if ($transition->[+FROM] eq $self->{'state'}) { 00107 if (@match = ($self->{'line'} =~ /$transition->[+CONDITION]/)) { 00108 # Fill-in blank lines until next output line matches 00109 # current input line. Otherwise warnings and errors 00110 # of Doxygen cannot be easily related to the input source. 00111 $self->_append('', 'blank') until @{$self->{'output'}} >= $self->{'lineno'} - 1; 00112 # perform action of transition 00113 $self->{'transition'} = $transition; 00114 $transition->[+ACTION]->($self, @match) if defined $transition->[+ACTION]; 00115 # keep track of visited states 00116 push @{$self->{'history'}}, $self->{'state'} 00117 unless $self->{'history'}->[-1] eq $self->{'state'}; 00118 # transition to next state 00119 $self->{'state'} = $transition->[+TO]; 00120 last; 00121 } 00122 } 00123 } 00124 } 00125 close FILE; 00126 } 00127 00128 # ---------------------------------------------------------------------------- 00129 ## @brief Get filter output. 00130 sub output 00131 { 00132 my $self = shift; 00133 my $output = ''; 00134 foreach my $line (@{$self->{'output'}}) { 00135 $output .= $line->[+CODE] . "\n"; 00136 } 00137 return $output; 00138 } 00139 00140 # ============================================================================ 00141 # protected 00142 # ============================================================================ 00143 00144 # ---------------------------------------------------------------------------- 00145 ## @brief Append line to output. 00146 sub _append 00147 { 00148 my $self = shift; 00149 my $line = shift; 00150 push @{$self->{'output'}}, [$line, [@_]]; 00151 } 00152 00153 # ---------------------------------------------------------------------------- 00154 ## @brief Handle none-blank line. 00155 # 00156 # This action inserts a dummy class definition which is ignored by Doxygen 00157 # if the previous block was a Doxygen comment that is not associated with 00158 # any following declaration. Otherwise, another transition would have handled 00159 # this declaration before. 00160 sub _noneblank 00161 { 00162 my $self = shift; 00163 if ($self->{'history'}->[-1] eq 'doxydoc') { 00164 $self->_append("class DO_NOT_MERGE_WITH_FOLLOWING_COMMENT;", 'prevent-merge'); 00165 } 00166 } 00167 00168 00169 # ---------------------------------------------------------------------------- 00170 ## @brief Start of Doxygen comment. 00171 sub _doxydoc_begin 00172 { 00173 my ($self, $comment) = @_; 00174 $self->{'params'} = []; 00175 $self->{'returndoc'} = 0; 00176 $self->{'returndoc'} = 1 if $comment =~ /[\@]returns?\s+/; 00177 $self->_doxydoc_comment($comment); 00178 } 00179 00180 # ---------------------------------------------------------------------------- 00181 ## @brief Doxygen comment line. 00182 sub _doxydoc_comment 00183 { 00184 my ($self, $comment) = @_; 00185 $self->_append("///$comment", 'doxydoc'); 00186 } 00187 00188 # ---------------------------------------------------------------------------- 00189 ## @brief Doxygen parameter documentation. 00190 # 00191 # The documentation lines which document function/method/macro parameters 00192 # are extracted and the information stored in the filter object. These parameter 00193 # documentations can then be used by the particular Doxygen filter to generate 00194 # a proper parameter list in case of languages which do by themselves not 00195 # explicitly specify the type and name of the function parameters such as in 00196 # Perl and Bash, in particular. Moreover, CMake provides the special ARGN 00197 # parameter which stores all additional unnamed arguments. 00198 sub _doxydoc_param 00199 { 00200 my ($self, $prefix, $dir, $name, $comment) = @_; 00201 $dir = '' if not defined $dir; 00202 $self->_append("///" . $prefix . "param$dir $name $comment", 'doxydoc', 'param'); 00203 if ($dir =~ /out/ and $dir =~ /in/) { $dir = 'inout'; } 00204 elsif ($dir =~ /out/) { $dir = 'out'; } 00205 else { $dir = 'in'; } 00206 push @{$self->{'params'}}, {'dir' => $dir, 'name' => $name}; 00207 } 00208 00209 # ---------------------------------------------------------------------------- 00210 ## @brief Doxygen return value documentation. 00211 # 00212 # This function simply records in the 'returndoc' member of the filter that 00213 # a "@returns" or "\returns" Doxygen is present in the current Doxygen comment. 00214 # Some filters such as the one for CMake or Bash, use a pseudo return type 00215 # which indicates the type of the function rather than the actual type of 00216 # a return value. Often these functions do not return any particular value. 00217 # In this case, if the Doxygen comment does not include a documentation for 00218 # the pseudo return value, Doxygen will warn. To avoid this warning, a standard 00219 # documentation for the pseudo return value may be added by the filter. 00220 sub _doxydoc_returns 00221 { 00222 my ($self, $comment) = @_; 00223 $self->{'returndoc'} = 1; 00224 $self->_doxydoc_comment($comment); 00225 } 00226 00227 # ---------------------------------------------------------------------------- 00228 ## @brief End of Doxygen comment. 00229 sub _doxydoc_end 00230 { 00231 my $self = shift; 00232 # mark current line that it needs to be reprocessed as this transition 00233 # only leaves the current state but another transition may actually apply 00234 $self->{'reprocess'} = 1; 00235 } 00236 00237 00238 1;