File Coverage

blib/lib/Autodia/Handler/Perl.pm
Criterion Covered Total %
statement 15 421 3.5
branch 0 222 0.0
condition 0 73 0.0
subroutine 5 11 45.4
pod 0 1 0.0
total 20 728 2.7


line stmt bran cond sub pod time code
1             package Autodia::Handler::Perl;
2             require Exporter;
3 1     1   1924 use strict;
  1         3  
  1         51  
4              
5             =head1 NAME
6              
7             Autodia::Handler::Perl.pm - AutoDia handler for perl
8              
9             =head1 DESCRIPTION
10              
11             HandlerPerl parses files into a Diagram Object, which all handlers use. The role of the handler is to parse through the file extracting information such as Class names, attributes, methods and properties.
12              
13             HandlerPerl parses files using simple perl rules. A possible alternative would be to write HandlerCPerl to handle C style perl or HandleHairyPerl to handle hairy perl.
14              
15             HandlerPerl is registered in the Autodia.pm module, which contains a hash of language names and the name of their respective language - in this case:
16              
17             %language_handlers = { .. , perl => "perlHandler", .. };
18              
19             =cut
20              
21 1     1   6 use Data::Dumper;
  1         3  
  1         94  
22              
23 1     1   7 use vars qw($VERSION @ISA @EXPORT);
  1         2  
  1         73  
24 1     1   5 use Autodia::Handler;
  1         2  
  1         50  
25              
26             @ISA = qw(Autodia::Handler Exporter);
27              
28 1     1   5 use Autodia::Diagram;
  1         3  
  1         7262  
29              
30             =head1 METHODS
31              
32             =head2 CONSTRUCTION METHOD
33              
34             use Autodia::Handler::Perl;
35              
36             my $handler = Autodia::Handler::Perl->New(\%Config);
37              
38             This creates a new handler using the Configuration hash to provide rules selected at the command line.
39              
40             =head2 ACCESSOR METHODS
41              
42             $handler->Parse(filename); # where filename includes full or relative path.
43              
44             This parses the named file and returns 1 if successful or 0 if the file could not be opened.
45              
46             $handler->output(); # any arguments are ignored.
47              
48             This outputs the Dia XML file according to the rules in the %Config hash passed at initialisation of the object.
49              
50             =cut
51              
52             sub find_files_by_packagename {
53 0     0 0   my $config = shift;
54 0           my $args = $config->{args};
55 0           my @filenames = ();
56 0           die "not implemented yet, sorry\n";
57 0           my @incdirs = @INC;
58 0 0         if ($config) {
59 0           unshift (@incdirs, split(" ",$args->{'d'}));
60             }
61              
62 0           my @regexen = map ( s|::|\/|g, split(" ",$args->{'i'}));
63             find ( { wanted => sub {
64 0 0   0     unless (-d) {
65 0           foreach my $regex (@regexen) {
66 0 0         push @filenames, $File::Find::name
67             if ($File::Find::name =~ m/$regex/);
68             }
69             }
70             },
71             preprocess => sub {
72 0     0     my @return;
73 0           foreach (@_) {
74 0 0 0       push(@return,$_) unless (m/^.*\/?(CVS|RCS)$/ && $config->{skipcvs});
75             }
76 0           return @return;
77             },
78             },
79             @incdirs
80 0           );
81 0           return @filenames;
82             }
83              
84             #------------------------------------------------------------------------
85             # Access Methods
86              
87             # parse_file inherited from Autodia::Handler
88              
89             #-----------------------------------------------------------------------------
90             # Internal Methods
91              
92             # _initialise inherited from Autodia::Handler
93              
94             sub _parse {
95 0     0     my $self = shift;
96 0           my $fh = shift;
97 0           my $filename = shift;
98 0           my $Diagram = $self->{Diagram};
99 0           my $pkg_regexp = '[A-Za-z][\w:]+';
100 0           my $Class;
101              
102             # Class::Tangram bits
103 0           $self->{_superclasses} = {};
104 0           $self->{_modules} = {};
105 0           $self->{_is_tangram_class} = {};
106 0           $self->{_in_tangram_class} = 0;
107 0           $self->{_insideout_class} = 0;
108 0           my $pat1 = '[\'\"]?\w+[\'\"]?\s*=>\s*\{.*?\}';
109 0           my $pat2 = '[\'\"]?\w+[\'\"]?\s*=>\s*undef';
110              
111             # pod
112 0           $self->{pod} = 0;
113              
114             # parse through file looking for stuff
115 0           my $continue = {};
116 0           my $last_sub;
117              
118 0           my $line_no = 0;
119 0           foreach my $line (<$fh>) {
120 0           $line_no++;
121 0           chomp $line;
122 0 0         if ($self->_discard_line($line)) {
123             # warn "discarded line : $line\n";
124 0           next;
125             }
126              
127             # if line contains package name then parse for class name
128 0 0 0       if ($line =~ /^\s*package\s+($pkg_regexp)?;?/ || $continue->{package}) {
129 0 0         $line =~ /^\s*($pkg_regexp);/ if($continue->{package});
130 0 0         if(!$1) {
131 0           warn "No package name! line $line_no : $line\n";
132 0           $continue->{package} = 1;
133 0           next;
134             }
135              
136 0           $continue->{package} = 0;
137 0           my $className = $1;
138 0 0         last if ($self->skip($className));
139             # create new class with name
140 0           $Class = Autodia::Diagram::Class->new($className);
141             # add class to diagram
142 0           my $exists = $Diagram->add_class($Class);
143 0 0         $Class = $exists if ($exists);
144             }
145              
146 0           my $continue_base = $continue->{base};
147 0 0 0       if ($line =~ /^\s*use\s+(?:base|parent)\s+(?:q|qw|qq)?\s*([\'\"\(\{\/\#])\s*([^\'\"\)\}\/\#]*)\s*(\1|[\)\}])?/ or ($continue_base && $line =~ /$continue_base/)) {
      0        
148              
149 0           my $superclass = $2;
150 0   0       my $end = $3 || '';
151              
152 0 0         if ($continue_base) {
153             # warn "continuing base\n";
154 0           $continue_base =~ s/[\)\}\'\"]/\\1/;
155             # warn "base ctd : $continue_base\n";
156             # warn "superclass : " . ($superclass|| '') . "\n";
157              
158 0 0         if ( $line =~ /(.*)\s*$continue_base?/ ) {
159 0           $continue_base = 0;
160 0           $superclass = $1;
161             # warn "end of continued base\n";
162             }
163             } else {
164             # warn "start of base\n";
165             # warn "superclass : $superclass\n";
166 0           $continue_base = '[\)\}\'\"]';
167 0 0         if ($end) {
168 0           $continue_base = 0;
169             # warn "base is only 1 line\n";
170             }
171             # warn "continue base : $continue_base\n";
172             }
173             # warn "superclass : $superclass\n";
174 0           $continue->{base} = $continue_base;
175             # check package exists before doing stuff
176 0           $self->_is_package(\$Class, $filename);
177              
178 0           my @superclasses = split(/[\s*,]/, $superclass);
179              
180 0           foreach my $super (@superclasses) # WHILE_SUPERCLASSES
181             {
182             # discard if stopword
183 0 0         next if ($super =~ /(?:exporter|autoloader)/i);
184             # create superclass
185 0           my $Superclass = Autodia::Diagram::Superclass->new($super);
186             # add superclass to diagram
187 0           $self->{_superclasses}{$Class->Name}{$super} = 1;
188 0 0         if ($super =~ m/Class..Accessor\:*/) {
189 0           $self->{_superclasses}{$Class->Name}{'Class::Accessor'} = 1;
190             }
191              
192 0 0         $self->{_is_tangram_class}{$Class->Name} = {state=>0} if ($super eq 'Class::Tangram');
193              
194 0           my $exists_already = $Diagram->add_superclass($Superclass);
195             # warn "already exists ? $exists_already \n";
196 0 0         if (ref $exists_already) {
197 0           $Superclass = $exists_already;
198             }
199             # create new inheritance
200 0           my $Inheritance = Autodia::Diagram::Inheritance->new($Class, $Superclass);
201             # add inheritance to superclass
202 0           $Superclass->add_inheritance($Inheritance);
203             # add inheritance to class
204 0           $Class->add_inheritance($Inheritance);
205             # add inheritance to diagram
206 0           $Diagram->add_inheritance($Inheritance);
207             }
208              
209              
210 0 0         if (grep (/DBIx::Class$/,@superclasses)) {
211 0           $self->{_dbix_class} = 1;
212             }
213 0           next;
214             }
215              
216             # if line contains dependancy name then parse for module name
217 0 0         if ($line =~ /^\s*(use|require)\s+($pkg_regexp)/) {
218             # warn "found a module being used/required : $2\n";
219 0 0         unless (ref $Class) {
220             # create new class with name
221 0           $Class = Autodia::Diagram::Class->new($filename);
222             # add class to diagram
223 0           my $exists = $Diagram->add_class($Class);
224 0 0         $Class = $exists if ($exists);
225             }
226 0           my $componentName = $2;
227             # discard if stopword
228 0 0         next if ($componentName =~ /^(strict|vars|exporter|autoloader|warnings.*|constant.*|data::dumper|carp.*|overload|switch|\d|lib)$/i);
229              
230 0 0         if ($componentName eq 'Class::XSAccessor') {
231 0           $self->{_class_xsaccessor} = 1;
232             }
233              
234 0 0         if ($componentName eq 'Object::InsideOut') {
235 0           $self->{_insideout_class} = 1;
236 0 0         if ($line =~ /^\s*use\s+.*qw\((.*)\)/) {
237 0           my @superclasses = split(/[\s+]/, $1);
238 0           foreach my $super (@superclasses) {
239 0           my $Superclass = Autodia::Diagram::Superclass->new($super);
240             # add superclass to diagram
241 0           my $exists_already = $Diagram->add_superclass($Superclass);
242             # warn "already exists ? $exists_already \n";
243 0 0         if (ref $exists_already) {
244 0           $Superclass = $exists_already;
245             }
246             # create new inheritance
247 0           my $Inheritance = Autodia::Diagram::Inheritance->new($Class, $Superclass);
248             # add inheritance to superclass
249 0           $Superclass->add_inheritance($Inheritance);
250             # add inheritance to class
251 0           $Class->add_inheritance($Inheritance);
252             # add inheritance to diagram
253 0           $Diagram->add_inheritance($Inheritance);
254             }
255             }
256 0           next;
257             }
258              
259 0           $self->{_modules}{$componentName} = 1;
260              
261             # check package exists before doing stuff
262 0           $self->_is_package(\$Class, $filename);
263              
264 0           my $continue_fields = $continue->{fields};
265 0 0 0       if ($line =~ /\s*use\s+(fields|private|public)\s+(?:q|qw|qq){0,1}\s*([\'\"\(\{\/\#])\s*(.*)\s*([\)\}\1]?)/ or $continue_fields) {
266 0           my ($pragma,$fields) = ($1,$3);
267             # warn "pragma : $pragma .. fields : $fields\n";
268 0 0         if ($continue_fields) {
269 0           $continue_fields =~ s/[\)\}\'\"]/\\1/;
270             # warn "fields ctd : $continue_fields\n";
271 0 0         if ( $line =~ m/(.*)\s*$continue_fields?/ ) {
272 0           $continue_fields = 0;
273 0           $fields = $1;
274             }
275             } else {
276 0           $continue_fields = '[\)\}\'\"]';
277 0 0         if ($fields =~ /(.*)([\)\}\1])/) {
278 0           $continue_fields = 0;
279 0           $fields = $1;
280             }
281             # warn "continue fields : $continue_fields\n";
282             }
283             # warn "fields : $fields\n";
284              
285 0           my @fields = split(/\s+/,$fields);
286 0           foreach my $field (@fields) {
287             # warn "fields : $field\n";
288 0 0         my $attribute_visibility = ( $field =~ m/^\_/ ) ? 1 : 0;
289 0 0         unless ($pragma eq 'fields') {
290 0 0         $attribute_visibility = ($pragma eq 'private' ) ? 1 : 0;
291             }
292             $Class->add_attribute({
293 0 0         name => $field,
294             visibility => $attribute_visibility,
295             Id => $Diagram->_object_count,
296             }) unless ($field =~ /^\$/);
297             }
298             } else {
299             # create component
300 0           my $Component = Autodia::Diagram::Component->new($componentName);
301             # add component to diagram
302 0           my $exists = $Diagram->add_component($Component);
303              
304             # replace component if redundant
305 0 0         if (ref $exists) {
306 0           $Component = $exists;
307             }
308             # create new dependancy
309 0           my $Dependancy = Autodia::Diagram::Dependancy->new($Class, $Component);
310             # add dependancy to diagram
311 0           $Diagram->add_dependancy($Dependancy);
312             # add dependancy to class
313 0           $Class->add_dependancy($Dependancy);
314             # add dependancy to component
315 0           $Component->add_dependancy($Dependancy);
316 0           next;
317             }
318 0           $continue->{fields} = $continue_fields;
319             }
320              
321             # if ISA in line then extract templates/superclasses
322 0 0         if ($line =~ /^\s*\@(?:\w+\:\:)*ISA\s*\=\s*(?:q|qw){0,1}\((.*)\)/) {
323 0           my $superclass = $1;
324 0           $superclass =~ s/[\'\",]//g;
325              
326             # warn "handling superclasses $1 with \@ISA\n";
327             # warn "superclass line : $line \n";
328 0 0         if ($superclass) {
329             # check package exists before doing stuff
330 0           $self->_is_package(\$Class, $filename);
331              
332 0           my @superclasses = split(" ", $superclass);
333              
334 0           foreach my $super (@superclasses) # WHILE_SUPERCLASSES
335             {
336             # discard if stopword
337 0 0 0       next if ($super =~ /(?:exporter|autoloader)/i || !$super);
338             # create superclass
339 0           my $Superclass = Autodia::Diagram::Superclass->new($super);
340             # add superclass to diagram
341 0           my $exists_already = $Diagram->add_superclass($Superclass);
342             # warn "already exists ? $exists_already \n";
343 0 0         if (ref $exists_already) {
344 0           $Superclass = $exists_already;
345             }
346 0           $self->{_superclasses}{$Class->Name}{$super} = 1;
347 0 0         $self->{_is_tangram_class}{$Class->Name} = {state=>0} if ($super eq 'Class::Tangram');
348             # create new inheritance
349             # warn "creating inheritance from superclass : $super\n";
350 0           my $Inheritance = Autodia::Diagram::Inheritance->new($Class, $Superclass);
351             # add inheritance to superclass
352 0           $Superclass->add_inheritance($Inheritance);
353             # add inheritance to class
354 0           $Class->add_inheritance($Inheritance);
355             # add inheritance to diagram
356 0           $Diagram->add_inheritance($Inheritance);
357             }
358             } else {
359 0           warn "ignoring empty \@ISA line $line_no \n";
360             }
361             }
362              
363 0 0 0       if ($self->{_modules}{Moose} && $line =~ m/extends (?:q|qw|qq)?\s*([\'\"\(\{\/\#])\s*([^\'\"\)\}\/\#]*)\s*(\1|[\)\}])?/ ) {
364 0           my $superclass = $2;
365 0           my @superclasses = split(/[\s*,]/, $superclass);
366              
367 0           foreach my $super (@superclasses) # WHILE_SUPERCLASSES
368             {
369 0           my $Superclass = Autodia::Diagram::Superclass->new($super);
370             # add superclass to diagram
371 0           $self->{_superclasses}{$Class->Name}{$super} = 1;
372 0           my $exists_already = $Diagram->add_superclass($Superclass);
373             # warn "already exists ? $exists_already \n";
374 0 0         if (ref $exists_already) {
375 0           $Superclass = $exists_already;
376             }
377             # create new inheritance
378 0           my $Inheritance = Autodia::Diagram::Inheritance->new($Class, $Superclass);
379             # add inheritance to superclass
380 0           $Superclass->add_inheritance($Inheritance);
381             # add inheritance to class
382 0           $Class->add_inheritance($Inheritance);
383             # add inheritance to diagram
384 0           $Diagram->add_inheritance($Inheritance);
385             }
386             }
387              
388             # Handle Class::Tangram classes
389 0 0         if (ref $self) {
390 0 0 0       if ($line =~ /^\s*(?:our|my)?\s+\$fields\s(.*)$/ and defined $self->{_is_tangram_class}{$Class->Name}) {
391 0           $self->{_field_string} = '';
392             # warn "tangram parser : found start of fields for ",$Class->Name,"\n";
393 0           $self->{_field_string} = $1;
394             # warn "field_string : $self->{_field_string}\n";
395 0           $self->{_in_tangram_class} = 1;
396 0 0         if ( $line =~ /^(.*\}\s*;)/) {
397             # warn "found end of fields for ",$Class->Name,"\n";
398 0           $self->{_in_tangram_class} = 2;
399             }
400             }
401 0 0         if ($self->{_in_tangram_class}) {
402              
403 0 0 0       if ( $line =~ /^(.*\}\s*;)/ && $self->{_in_tangram_class} == 1) {
404             # warn "found end of fields for ",$Class->Name,"\n";
405 0           $self->{_field_string} .= $1;
406 0           $self->{_in_tangram_class} = 2;
407             } else {
408             # warn "adding line to fields for ",$Class->Name,"\n";
409 0 0         $self->{_field_string} .= $line unless ($self->{_in_tangram_class} == 2);
410             }
411 0 0         if ($self->{_in_tangram_class} == 2) {
412             # warn "processing fields for ",$Class->Name,"\n";
413 0           $_ = $self->{_field_string};
414 0           s/^\s*\=\s*\{\s//;
415 0           s/\}\s*;$//;
416 0           s/[\s\n]+/ /g;
417             # warn "fields : $_\n";
418 0           my %field_types = m/(\w+)\s*=>\s*[\{\[]\s*($pat1|$pat2|qw\([\w\s]+\))[\s,]*[\}\]]\s*,?\s*/g;
419              
420             # warn Dumper(field_types=>%field_types);
421 0           foreach my $field_type (keys %field_types) {
422             # warn "handling $field_type..\n";
423 0           $_ = $field_types{$field_type};
424 0           my $pat1 = '\'\w+\'\s*=>\s*\{.*?\}';
425 0           my $pat2 = '\'\w+\'\s*=>\s*undef';
426 0           my %fields;
427 0 0         if (/qw\((.*)\)/) {
428 0           my $fields = $1;
429             # warn "qw fields : $fields\n";
430 0           my @fields = split(/\s+/,$fields);
431 0           @fields{@fields} = @fields;
432             } else {
433 0           %fields = m/[\'\"]?(\w+)[\'\"]?\s*=>\s*([\{\[].*?[\}\]]|undef)/g;
434             }
435             # warn Dumper(fields=>%fields);
436 0           foreach my $field (keys %fields) {
437             # warn "found field : '$field' of type '$field_type' in (class ",$Class->Name,") : \n";
438 0           my $attribute = { name=>$field, type=>$field_type, Id => $Diagram->_object_count, };
439 0 0         if ($fields{$field} =~ /class\s*=>\s*[\'\"](.*?)[\'\"]/) {
440 0           $attribute->{type} = $1;
441             }
442 0 0         if ($fields{$field} =~ /init_default\s*=>\s*[\'\"](.*?)[\'\"]/) {
443 0           $attribute->{default} = $1;
444             # FIXME : attribute default values unsupported ?
445             }
446 0 0         $attribute->{visibility} = ( $attribute->{name} =~ m/^\_/ ) ? 1 : 0;
447              
448 0           $Class->add_attribute($attribute);
449             }
450              
451             }
452 0           $self->{_in_tangram_class} = 0;
453             }
454             }
455              
456             }
457              
458             # handle Class::DBI/Ima::DBI
459 0 0         if ($line =~ /->columns\(\s*All\s*=>\s*(.*)$/) {
460 0           my $columns = $1;
461 0           my @cols;
462 0 0         if ($columns =~ s/^qw(.)//) {
    0          
463 0           $columns =~ s/\s*[\)\]\}\/\#\|]\s*\)\s*;\s*(#.*)?$//;
464 0           @cols = split(/\s+/,$columns);
465             } elsif ($columns =~ /'.+'/) {
466 0           @cols = map( /'(.*)'/ ,split(/\s*,\s*/,$columns));
467             } else {
468 0           warn "can't parse CDBI style columns line $line_no\n";
469 0           next;
470             }
471              
472 0           foreach my $col ( @cols ) {
473             # add attribute
474 0 0         my $visibility = ( $col =~ m/^\_/ ) ? 1 : 0;
475 0           $Class->add_attribute({
476             name => $col,
477             visibility => $visibility,
478             Id => $Diagram->_object_count,
479             });
480             # add accessor
481 0           $Class->add_operation({ name => $col, visibility => $visibility, Id => $Diagram->_object_count() } );
482             }
483              
484 0 0         $continue->{cdbi_cols} = 1 unless $line =~ s/(.*)\)\s*;(#.*)?\s*$/$1/;
485 0           next;
486             }
487              
488             # handle Class::Data::Inheritable
489             # Stuff->mk_classdata(
490 0 0 0       if ( $Class && $self->{_superclasses}{$Class->Name}{'Class::Data::Inheritable'} ) {
491 0 0         if ($line =~ /->mk_classdata\((\w+)/) {
492 0           my $attribute = $1;
493 0 0         my $visibility = ( $attribute =~ m/^\_/ ) ? 1 : 0;
494 0           $Class->add_attribute({
495             name => $attribute,
496             visibility => $visibility,
497             Id => $Diagram->_object_count,
498             });
499             # add accessor
500 0           $Class->add_operation({ name => $attribute, visibility => $visibility, Id => $Diagram->_object_count() } );
501             }
502             }
503              
504 0 0 0       if ( $Class && $self->{_superclasses}{$Class->Name}{'Class::Accessor'} ) {
505             # handle Class::Accessor
506 0 0         if ($line =~ /->mk_accessors\s*\(\s*(.*)$/) {
507 0           my $attributes = $1;
508 0           my @attributes;
509 0 0         if ($attributes =~ s/^qw(.)//) {
    0          
510 0           $attributes =~ s/\s*[\)\]\}\/\#\|]\s*\)\s*;\s*(#.*)?$//;
511 0           @attributes = split(/\s+/,$attributes);
512             } elsif ($attributes =~ /'.+'/) {
513 0           @attributes = map( /'(.*)'/ ,split(/\s*,\s*/,$attributes));
514             } else {
515 0           warn "can't parse CDBI style attributes line $line_no\n";
516 0           next;
517             }
518              
519 0           foreach my $attribute ( @attributes ) {
520             # add attribute
521 0 0         next unless ($attribute =~ m/\w+/);
522 0 0         my $visibility = ( $attribute =~ m/^\_/ ) ? 1 : 0;
523 0           $Class->add_attribute({
524             name => $attribute,
525             visibility => $visibility,
526             Id => $Diagram->_object_count,
527             });
528             # add accessor if not already present
529 0 0         unless ($Class->get_operation($attribute)) {
530 0           $Class->add_operation({ name => $attribute, visibility => $visibility, Id => $Diagram->_object_count() } );
531             }
532             }
533              
534 0 0         $continue->{class_accessor_attributes} = 1 unless $line =~ s/(.*)\)\s*;(#.*)?\s*$/$1/;
535 0           next;
536             }
537             }
538              
539              
540 0 0         if ($continue->{class_accessor_attributes}) {
541 0           my @attributes;
542 0 0         $continue->{class_accessor_attributes} = 0 if $line =~ s/(.*)\)\s*;(#.*)?\s*$/$1/;
543 0 0         if ($line =~ /'.+'/) {
544 0           $line =~ s/\s*[\)\]\}\/\#\|]\s*$//;
545 0           @attributes = map( /'(.*)'/ ,split(/\s*,\s*/,$line));
546             } else {
547 0           @attributes = split(/\s+/,$line);
548             }
549 0           foreach my $attribute ( @attributes ) {
550 0 0         next unless ($attribute =~ m/\w+/);
551             # add attribute
552 0 0         my $visibility = ( $attribute =~ m/^\_/ ) ? 1 : 0;
553 0           $Class->add_attribute({
554             name => $attribute,
555             visibility => $visibility,
556             Id => $Diagram->_object_count,
557             });
558              
559             # add accessor if not already present
560 0 0         unless ($Class->get_operation($attribute)) {
561 0           $Class->add_operation({ name => $attribute, visibility => $visibility, Id => $Diagram->_object_count() } );
562             }
563             }
564             }
565              
566              
567             # handle Params::Validate
568 0 0 0       if ($last_sub && $self->{_modules}{'Params::Validate'} && ( $line =~ m/validate(_pos)?\s*\(/ or $self->{_in_params_validate_arguments} )) {
      0        
      0        
569 0           my $found_end = 0;
570             # warn "found params::validate for sub $last_sub \n line : $line\n";
571 0           $self->{_in_params_validate_arguments} = 1;
572 0 0         $self->{_in_params_validate_positional_arguments} = 1 if $line =~ m/validate_pos/ ;
573 0 0         if ($line =~ m|\)\s*;|) {
574 0           $found_end = 1;
575 0           $line =~ s/\)\s*;.*//;
576             # warn "found end \n";
577             }
578 0           $self->{_params_validate_arguments} .= $line;
579              
580 0 0         if ($found_end) {
581 0           my $validate_text = $self->{_params_validate_arguments};
582             # warn "found params::validate text : $validate_text\n";
583              
584             # process with eval ala data::dumper
585 0           $validate_text =~ s/.*validate\w*\s*\(\s*\@_\s*,//;
586             # warn "evaluating params::validate text : $validate_text\n";
587 0           my $params = eval $validate_text;
588             # warn Dumper $params;
589 0           my $parameters = [];
590 0 0         push (@$parameters, { Name => "(HASHREF)" }) unless ( $self->{_in_params_validate_positional_arguments} );
591 0           foreach my $param_name (keys %$params) {
592 0           my $parameter = { Name => $param_name };
593 0 0 0       if (ref $params->{$param_name} && ( $params->{type} || $params->{isa} ) ) {
      0        
594 0   0       $parameter->{Type} = $params->{type} || $params->{isa};
595             }
596 0           push (@$parameters, $parameter);
597             }
598 0 0         if (scalar @$parameters) {
599 0           my $operation = $Class->get_operation($last_sub);
600 0   0       $operation->{Params} ||= [];
601 0           push (@{$operation->{Params}}, @$parameters);
  0            
602 0           $Class->update_operation($operation);
603             }
604            
605 0           delete $self->{_params_validate_arguments};
606 0           $self->{_in_params_validate_arguments} = 0;
607 0           $self->{_in_params_validate_positional_arguments} = 0;
608             }
609             }
610              
611            
612             # handle DBIx::Class
613 0 0         if ($self->{_dbix_class_columns}) {
614 0           my $found_end = 0;
615 0           $line =~ s/#.*$//;
616 0 0         if ($line =~ m|\);|) {
617 0           $found_end = 1;
618 0           $line =~ s/\);.*//;
619             }
620 0           $self->{_dbix_class_columns} .= $line;
621 0 0         if ($found_end) {
622 0           my $columns_text = $self->{_dbix_class_columns} . '}';
623             # warn "class : , ", $Class->Name, "\n";
624             # warn "columns text : $columns_text \n";
625             # process with eval ala data::dumper
626 0           my $columns = eval $columns_text;
627             # warn Dumper $columns;
628 0           foreach my $attr_name (keys %$columns) {
629 0           $Class->add_attribute({
630             name => $attr_name,
631             visibility => 0,
632             Id => $Diagram->_object_count,
633             type => $columns->{$attr_name}{data_type},
634             });
635             }
636              
637 0           delete $self->{_dbix_class_columns};
638 0           $self->{_dbix_class} = 0;
639             }
640              
641             }
642              
643             # if line is DBIx::Class relationship then parse out
644 0 0 0       if ($self->{_dbix_class_relation} or $line =~ /\-\>has_(many|one)\s*\((.*)/ or $line =~ /\-\>(belongs_to)\s*\((.*)/) {
      0        
645 0           my $found_end = 0;
646 0           $line =~ s/#.*$//;
647 0 0         if ($line =~ m|\);|) {
648 0           $found_end = 1;
649 0           $line =~ s/\);.*//;
650             }
651              
652 0 0 0       if ($line =~ /\-\>has_(many|one)\s*\((.*)/ or $line =~ /\-\>(belongs_to)\s*\((.*)/) {
653 0           my ($rel_type, $rel_data) = ($1,$2);
654 0           $rel_data =~ s/#.*$//;
655 0           $self->{_dbix_class_relation}{rel_data} = "{ $rel_data ";
656 0           $self->{_dbix_class_relation}{rel_type} = $rel_type;
657             } else {
658 0           $self->{_dbix_class_relation}{rel_data} .= $line;
659             }
660              
661 0 0         if ($found_end) {
662 0           my $reldata = $self->{_dbix_class_relation}{rel_data} . '}';
663 0           my ($rel_name,$related_classname) = split(/\s*(?:\=\>|,)\s*/,$reldata);
664 0           $related_classname =~ s/['"]//g;
665 0           $rel_name =~ s/^\W+//;
666 0           $rel_name =~ s/['"]//g;
667              
668 0 0         unless ($related_classname) {
669 0           warn "no related class in relation data : $reldata\n";
670 0           next;
671             }
672              
673             # warn "creating relation : $rel_name to $related_classname\n";
674              
675 0           my $Superclass = Autodia::Diagram::Superclass->new($related_classname);
676 0           my $exists_already = $self->{Diagram}->add_superclass($Superclass);
677 0 0         $Superclass = $exists_already if (ref $exists_already);
678              
679             # create new relationship
680 0           my $Relationship = Autodia::Diagram::Relation->new($Class, $Superclass);
681             # add Relationship to superclass
682 0           $Superclass->add_relation($Relationship);
683             # add Relationship to class
684 0           $Class->add_relation($Relationship);
685             # add Relationship to diagram
686 0           $self->{Diagram}->add_relation($Relationship);
687 0           $Class->add_operation({ name => $rel_name, visibility => 0, Id => $Diagram->_object_count() } );
688              
689             }
690 0           delete $self->{_dbix_class_relation};
691             }
692              
693             # if line is DBIx::Class column metadata then parse out
694 0 0 0       if ($self->{_dbix_class} && $line =~ m/add_columns\s*\((.*)/) {
695 0           my $field_data = $1;
696 0           $field_data =~ s/#.*$//;
697 0           $self->{_dbix_class_columns} = "{ $field_data ";
698             }
699              
700             # if line is DBIx::Class component, then treat as superclass
701 0 0         if ($line =~ m/->load_components\s*\(\s*(?:q|qw|qq)?\s*([\'\"\(\{\/\#])\s*([^\'\"\)\}\/\#]*)\s*(\1|[\)\}])?/ ) {
702 0           my $component_string = $2;
703 0           foreach my $component_name (grep (/^\+/ , split(/[\s,]+/, $component_string ))) {
704 0           $component_name =~ s/['"+]//g;
705 0           my $Superclass = Autodia::Diagram::Superclass->new($component_name);
706 0           my $exists_already = $self->{Diagram}->add_superclass($Superclass);
707 0 0         $Superclass = $exists_already if (ref $exists_already);
708             # create new inheritance
709 0           my $Inheritance = Autodia::Diagram::Inheritance->new($Class, $Superclass);
710 0           $Superclass->add_inheritance($Inheritance);
711             # add inheritance to class
712 0           $Class->add_inheritance($Inheritance);
713             # add inheritance to diagram
714 0           $self->{Diagram}->add_inheritance($Inheritance);
715             }
716             }
717              
718              
719             # add Moose attributes
720 0 0 0       if ($self->{_modules}{Moose} && $line =~ /^\s*has\s+'?(\w+)'?/) {
721 0           my $attr_name = $1;
722 0           $Class->add_attribute({
723             name => $attr_name,
724             visibility => 0,
725             Id => $Diagram->_object_count,
726             });
727             }
728              
729              
730 0 0         if ( $self->{_class_xsaccessor} ) {
731              
732             }
733              
734             # if line is Object::InsideOut metadata then parse out
735 0 0 0       if ($self->{_insideout_class} && $line =~ /^\s*my\s+\@\w+\s+\:FIELD\s*\((.*)\)/) {
736 0           my $field_data = $1;
737 0           $field_data =~ s/['"\s]//g;
738 0           my %field_data = split( /\s*(?:=>|,)\s*/, $field_data);
739 0           (my $col = $field_data{Get} ) =~ s/get_//;
740 0           $Class->add_attribute({
741             name => $col,
742             visibility => 0,
743             Id => $Diagram->_object_count,
744             });
745 0           foreach my $key ( keys %field_data ) {
746             # add accessor/mutator
747 0 0         if ($key =~ m/(Get|Set|Acc|Mut|Com)/) {
748 0           $Class->add_operation({ name => $field_data{$key}, visibility => 0, Id => $Diagram->_object_count() } );
749             }
750             }
751              
752             }
753              
754             # if line contains sub then parse for method data
755 0 0         if ($line =~ /^\s*sub\s+?(\w+)/) {
756 0           my $subname = $1;
757              
758             # check package exists before doing stuff
759 0           $self->_is_package(\$Class, $filename);
760              
761 0           $subname =~ s/^(.*?)['"]\..*$/${1}_xxxx/;
762              
763 0           $last_sub = $subname;
764              
765 0           my %subroutine = ( "name" => $subname, );
766 0 0         $subroutine{"visibility"} = ($subroutine{"name"} =~ m/^\_/) ? 1 : 0;
767 0           $subroutine{"Id"} = $Diagram->_object_count();
768             # NOTE : perl doesn't provide named parameters
769             # if we wanted to be clever we could count the parameters
770             # see Autodia::Handler::PHP for an example of parameter handling
771              
772 0 0         unless ($Class->get_operation($subname)) {
773 0           $Class->add_operation(\%subroutine);
774             }
775              
776             }
777              
778             # if line contains object attributes parse add to class
779 0 0         if ($line =~ m/\$(class|self|this)\-\>\{['"]*(.*?)["']*}/) {
780 0           my $attribute_name = $2;
781 0           $attribute_name =~ s/^(.*?)['"]\..*$/${1}_xxxx/;
782 0           $attribute_name =~ s/['"\}\{\]\[]//g; # remove nasty badness
783 0 0         my $attribute_visibility = ( $attribute_name =~ m/^\_/ ) ? 1 : 0;
784              
785 0 0         $Class->add_attribute({
786             name => $attribute_name,
787             visibility => $attribute_visibility,
788             Id => $Diagram->_object_count,
789             }) unless ($attribute_name =~ /^\$/);
790             }
791              
792             }
793              
794 0           $self->{Diagram} = $Diagram;
795 0           close $fh;
796 0           return;
797             }
798              
799             sub _discard_line
800             {
801 0     0     my $self = shift;
802 0           my $line = shift;
803 0           my $discard = 0;
804              
805             SWITCH:
806             {
807 0 0         if ($line =~ m/^\s*$/) # if line is blank or white space discard
  0            
808             {
809 0           $discard = 1;
810 0           last SWITCH;
811             }
812              
813 0 0         if ($line =~ /^\s*\#/) # if line is a comment discard
814             {
815 0           $discard = 1;
816 0           last SWITCH;
817             }
818              
819 0 0         if ($line =~ /^\s*\=head/) # if line starts with pod syntax discard and flag with $pod
820             {
821 0           $self->{pod} = 1;
822 0           $discard = 1;
823 0           last SWITCH;
824             }
825              
826 0 0         if ($line =~ /^\s*\=cut/) # if line starts with pod end syntax then unflag and discard
827             {
828 0           $self->{pod} = 0;
829 0           $discard = 1;
830 0           last SWITCH;
831             }
832              
833 0 0         if ($self->{pod} == 1) # if line is part of pod then discard
834             {
835 0           $discard = 1;
836 0           last SWITCH;
837             }
838             }
839 0           return $discard;
840             }
841              
842             ####-----
843              
844             sub _is_package
845             {
846 0     0     my $self = shift;
847 0           my $package = shift;
848 0           my $Diagram = $self->{Diagram};
849              
850 0 0         unless(ref $$package)
851             {
852 0           my $filename = shift;
853             # create new class with name
854 0           $$package = Autodia::Diagram::Class->new($filename);
855             # add class to diagram
856 0           $Diagram->add_class($$package);
857             }
858              
859 0           return;
860             }
861              
862              
863             ###############################################################################
864              
865             =head1 SEE ALSO
866              
867             Autodia::Handler
868              
869             Autodia::Diagram
870              
871             =head1 AUTHOR
872              
873             Aaron Trevena, Eaaron.trevena@gmail.comE
874              
875             =head1 COPYRIGHT AND LICENSE
876              
877             Copyright (C) 2001-2007 by Aaron Trevena
878              
879             This library is free software; you can redistribute it and/or modify
880             it under the same terms as Perl itself, either Perl version 5.8.1 or,
881             at your option, any later version of Perl 5 you may have available.
882              
883             =cut
884              
885             1;
886              
887              
888              
889              
890