File Coverage

blib/lib/Autodia/Handler/Cpp.pm
Criterion Covered Total %
statement 12 263 4.5
branch 0 86 0.0
condition 0 10 0.0
subroutine 4 8 50.0
pod n/a
total 16 367 4.3


line stmt bran cond sub pod time code
1             ################################################################
2             # AutoDIA - Automatic Dia XML. (C)Copyright 2001 A Trevena #
3             # #
4             # AutoDIA comes with ABSOLUTELY NO WARRANTY; see COPYING file #
5             # This is free software, and you are welcome to redistribute #
6             # it under certain conditions; see COPYING file for details #
7             ################################################################
8              
9             # Now actually works (ish) thanks to Ekkehard ! significant #
10             # amounts of this code contributed by Ekkehard Goerlach #
11              
12             package Autodia::Handler::Cpp;
13              
14             require Exporter;
15              
16 1     1   1258 use strict;
  1         2  
  1         36  
17              
18 1     1   5 use vars qw($VERSION @ISA @EXPORT);
  1         3  
  1         58  
19 1     1   5 use Autodia::Handler;
  1         2  
  1         46  
20              
21             @ISA = qw(Autodia::Handler Exporter);
22              
23 1     1   6 use Autodia::Diagram;
  1         2  
  1         11023  
24              
25             #---------------------------------------------------------------
26              
27             #####################
28             # Constructor Methods
29              
30             # new inherited from Autodia::Handler
31              
32             #------------------------------------------------------------------------
33             # Access Methods
34              
35             # parse_file inherited from Autodia::Handler
36              
37             #-----------------------------------------------------------------------------
38             # Internal Methods
39              
40             # _initialise inherited from Autodia::Handler
41              
42             sub _parse
43             {
44 0     0     my $self = shift;
45 0           my $fh = shift;
46 0           my $filename = shift;
47 0           my $Diagram = $self->{Diagram};
48              
49             # print "processing file : $filename \n";
50              
51 0           my $Class;
52              
53 0           $self->{current_package} = $filename;
54 0           $self->{privacy} = 0;
55 0           $self->{comment} = 0;
56 0           $self->{in_class} = 0;
57 0           $self->{in_declaration} = 0;
58 0           $self->{in_method} = 0;
59 0           $self->{brace_depth} = 0;
60              
61 0           my $i = 0;
62              
63             # parse through file looking for stuff
64 0           while (<$fh>)
65             {
66 0           LINE:
67             {
68 0           chomp(my $line=$_);
69 0 0         if ($self->_discard_line($line)) { last LINE; }
  0            
70              
71             # print "line $i : $line \n";
72 0           $i++;
73              
74             # check for class declaration
75 0 0         if ($line =~ m/^\s*class\s+(\w+)/)
76             {
77              
78             # print "found class : $line \n";
79              
80 0           my $classname = $1;
81 0           $self->{in_class} = 1;
82 0           $self->{privacy} = "private";
83 0           $self->{visibility} = 1;
84 0           $classname =~ s/[\{\}]//g;
85 0 0         last if ($self->skip($classname));
86 0           $Class = Autodia::Diagram::Class->new($classname);
87 0           my $exists = $Diagram->add_class($Class);
88 0 0         $Class = $exists if ($exists);
89              
90             # handle superclass(es)
91 0 0         if ($line =~ m/^\s*class\s+\w+\s*\:\s*([^{]+)\s*/)
92             {
93 0           my $superclasses = $1;
94 0           $superclasses =~ s/public\s*//i;
95 0           warn "found superclasses : $superclasses\n";
96 0           my @superclasses = split (/\s*,\s*/, $superclasses);
97 0           foreach my $super (@superclasses) {
98 0           $super =~ s/\s*//ig;
99             # warn "superclass : $super\n";
100 0           $super =~ s/^\s*(\w+\s+)?([A-Za-z0-9\_]+)\s*$/$2/;
101             # warn "superclass : $super\n";
102 0           my $Superclass = Autodia::Diagram::Superclass->new($super);
103 0           my $exists_already = $Diagram->add_superclass($Superclass);
104 0 0         if (ref $exists_already) {
105 0           $Superclass = $exists_already;
106             }
107 0           my $Inheritance = Autodia::Diagram::Inheritance->new($Class, $Superclass);
108 0           $Superclass->add_inheritance($Inheritance);
109 0           $Class->add_inheritance($Inheritance);
110 0           $Diagram->add_inheritance($Inheritance);
111             }
112             }
113 0           last LINE;
114             }
115              
116             # check for end of class declaration
117 0 0 0       if ($self->{in_class} && ($line =~ m|^\s*\}\;|))
118             {
119             # print "found end of class\n";
120 0           $self->{in_class} = 0;
121 0           $self->{privacy} = 0;
122 0           last LINE;
123             }
124              
125             # check for abstraction/data hiding
126 0 0         if ($self->{in_class})
127             {
128 0 0         if ($line =~ m/^\s*protected\s*\:/)
129             {
130             # print "found protected variables/classes\n";
131 0           $self->{privacy} = "protected";
132 0           $self->{visibility} = 2;
133 0           $self->_parse_private_things($line,$Class);
134 0           last LINE;
135             }
136              
137 0 0         if ($line =~ m/^\s*private\s*\w*\:/)
138             {
139             # print "found private variables/classes\n";
140 0           $self->{privacy} = "private";
141 0           $self->{visibility} = 1;
142              
143             # check for attributes and methods
144 0           $self->_parse_private_things($line,$Class);
145              
146 0           last LINE;
147             }
148              
149 0 0         if ($line =~ m/^\s*public\s*\w*\:/)
150             {
151             # print "found public variables/classes\n";
152 0           $self->{privacy} = "public";
153 0           $self->{visibility} = 0;
154 0           $self->_parse_private_things($line,$Class);
155 0           last LINE;
156             }
157              
158 0 0         if ($line =~ m/operator/)
159             {
160             # print "found overloaded operator\n";
161 0 0         last LINE if $line =~ /;/;
162              
163 0           while ($line !~ /{/)
164             {
165 0           $line = <$fh>;
166             # print "waiting for start of overload def: $line\n";
167             }
168 0           my $start_brace_cnt = $line =~ tr/{/{/ ;
169 0           my $end_brace_cnt = $line =~ tr/}/}/ ;
170              
171 0           $self->{brace_depth} = $start_brace_cnt - $end_brace_cnt;
172 0 0         $self->{in_method} = 1 unless $self->{brace_depth} == 0;
173             # print "OvStart: ",$start_brace_cnt, $end_brace_cnt, $self->{brace_depth}, $self->{in_method} ,"\n";
174              
175 0           last LINE;
176             }
177              
178             # if inside a class method then discard line
179 0 0         if ($self->{in_method})
180             {
181             # count number of braces and increment decrement depth accordingly
182             # if depth = 0 then reset in_method and next;
183             # else next;
184 0           my $start_brace_cnt = $line =~ tr/{/{/ ;
185 0           my $end_brace_cnt = $line =~ tr/}/}/ ;
186              
187 0           $self->{brace_depth} = $self->{brace_depth} + $start_brace_cnt - $end_brace_cnt;
188 0 0         $self->{in_method} = $self->{brace_depth} == 0 ? 0 : 1;
189              
190             # print "In method: ",$start_brace_cnt, $end_brace_cnt, $self->{brace_depth}, $self->{in_method} ,"\n";
191 0           last LINE;
192             }
193              
194             # check for simple declarations
195             # space* const? space+ (namespace::)* type space* modifier? space+ name;
196              
197 0 0         if ($line =~ m/^\s*\w*?\s*((\w+\s*::\s*)*[\w<>]+\s*[\*&]?)\s*(\w+)\s*\;.*$/) # Added support for pointers/refs/namespaces
198             {
199 0           my $name = $3;
200 0           my $type = $1;
201             # print "found simple variable declaration : name = $name, type = $type\n";
202              
203             #my $visibility = ( $name =~ m/^\_/ ) ? 1 : 0;
204              
205 0           $Class->add_attribute({
206             name => $name,
207             visibility => $self->{visibility}, #was: $visibility,
208             type => $type,
209             });
210              
211 0           last LINE;
212             }
213              
214             # check for simple sub
215 0 0         if ($line =~ m/^ # start of line
216             \s* # whitespace
217             (\w*?\s*?(\w+\s*::\s*)*[\w<>]*?\s*[\*&]?) # type of the method: $1. Added support for namespaces
218             \s* # whitespace
219             (~?\w+) # name of the method: $3
220             \s* # whitespace
221             \(\s* # start of parameter list
222             ([:\w\,\s\*=&\"<>\\\d\-]*) # all parameters: $4
223             (\)?) # may be an ending bracket: $5
224             [\w\s=]*(;?) # possibly end of signature $6
225             .*$/x
226             )
227             {
228 0           my $name = $3;
229 0   0       my $type = $1 || "void";
230 0           my $params = $4;
231 0           my $end_bracket = $5;
232 0           my $end_semicolon = $6;
233              
234 0           my $have_continuation = 0;
235 0           my $have_end_semicolon= 0;
236              
237 0 0         if ($name eq $Class->{"name"})
238             {
239             # print "found constructor declaration : name = $name\n";
240 0           $type = "";
241             }
242             else
243             {
244 0 0         if ($name eq "~".$Class->{"name"})
245             {
246             # print "found destructor declaration : name = $name\n";
247 0           $type = "";
248             }
249             else
250             {
251             # print "found simple function declaration : name = $name, type = $type\n";
252             }
253             }
254              
255 0 0         $have_continuation = 1 unless $end_bracket eq ")";
256 0 0         $have_end_semicolon = 1 if $end_semicolon eq ";";
257              
258             # print $have_continuation ? "no ":"with " ,"end bracket : $end_bracket\n";
259             # print $have_end_semicolon ? "with ":"no " ,"end semicolon : $end_semicolon\n";
260              
261 0           $params =~ s|\s+$||;
262 0           my @params = split(",",$params);
263 0           my $pc = 0; # parameter count
264              
265 0           my %subroutine = (
266             name => $name,
267             type => $type,
268             visibility => $self->{visibility},
269             );
270              
271             # If we have continuation lines for the parameters get them all
272 0           while ($have_continuation)
273             {
274 0           my $line = <$fh>;
275 0 0         last unless ($line);
276 0           chomp $line;
277              
278 0 0         if ($line =~ m/^ # start of line
279             \s* # whitespace
280             ([:\w\,\|\s\*=&\"<>\\]*) # all parameters: $1
281             (\)?) # may be an ending bracket: $2
282             [\w\s=]*(;?) # possibly end of signature $3
283             .*$/x)
284             {
285 0           my $cparams = $1;
286 0           $end_bracket = $2;
287 0           $end_semicolon = $3;
288              
289 0           $cparams =~ s|\s+$||;
290 0           my @cparams = split(",",$cparams);
291 0           push @params, @cparams;
292              
293             # print "More parameters: >$cparams<\n";
294              
295 0 0         $have_continuation = 0 if ($end_bracket eq ")");
296 0 0         $have_end_semicolon = 1 if ($end_semicolon eq ";");
297              
298             # print $have_continuation ? "no ":"with " ,"end bracket : $end_bracket\n";
299             # print $have_end_semicolon ? "with ":"no " ,"end semicolon : $end_semicolon\n";
300             }
301             }
302              
303              
304             # then get parameters and types
305 0           my @parameters = ();
306             # print "All parameters: ",join(';',@params),"\n";
307 0           foreach my $parameter (@params)
308             {
309 0           $parameter =~ s/const\s+//;
310 0           $parameter =~ m/\s*((\w+::)*[\w<>]+\s*[\*|\&]?)\s*(\w+)/ ;
311 0           my ($type, $name) = ($1,$3);
312              
313 0           $type =~ s/\s//g;
314 0           $name =~ s/\s//g;
315              
316 0           $parameters[$pc] = {
317             Name => $name,
318             Type => $type,
319             };
320 0           $pc++;
321             }
322              
323 0           $subroutine{"Params"} = \@parameters;
324 0           $Class->add_operation(\%subroutine);
325              
326             # Now finished with parameters. If there was no end
327             # semicolon we have an inline method: we read on until we
328             # see the start of the method. This deals with (multi-line)
329             # constructor initialization lists as well.
330 0 0         last LINE if $have_end_semicolon;
331              
332 0           while ($line !~ /{/)
333             {
334 0           $line = <$fh>;
335 0           print "waiting for start of method def: $line\n";
336             }
337 0           my $start_brace_cnt = $line =~ tr/{/{/ ;
338 0           my $end_brace_cnt = $line =~ tr/}/}/ ;
339              
340 0           $self->{brace_depth} = $start_brace_cnt - $end_brace_cnt;
341 0 0         $self->{in_method} = 1 unless $self->{brace_depth} == 0;
342             # print "Start: ",$start_brace_cnt, $end_brace_cnt, $self->{brace_depth}, $self->{in_method} ,"\n";
343              
344 0           last LINE;
345             }
346              
347             # if line starts with word,space,word then its a declaration (probably)
348             # Broken.
349 0 0         if ($line =~ m/\s*[\w<>]+\s+(\w+\s*::\s*)*[\w<>]+/i)
350             {
351             # print " probably found a declaration : $line\n";
352 0           my @words = m/^(\w+)\s*[\(\,\;].*$/g;
353 0           my $name = $&;
354 0           my $rest = $';#' to placate some syntax highlighters
355 0           my $type = '';
356              
357 0           my $pc = 0; # point count (ie location in array)
358 0           foreach my $start_point (@-)
359             {
360 0           my $start = $start_point;
361 0           my $end = $+[$pc];
362 0           $type .= substr($line, $start, ($end - $start));
363 0           $pc++;
364             }
365              
366             # if next character is a ( then the line is a function declaration
367 0 0         if ($rest =~ m|^\(([\w<>]+)\(.*(\;?)\s*$|)
368             {
369             # print "probably found a function : $line \n";
370 0           my $params = $1;
371 0           my @params = split(",",$params);
372              
373 0           my $declaration = 0;
374 0 0         if (defined $2) # if line ends with ";" then its a declaration
375             {
376 0           $declaration = 1;
377 0           my @parameters = ();
378 0           my $pc = 0; # parameter count
379 0           my %subroutine = (
380             name => $name,
381             type => $type,
382             visibility => $self->visibility,
383             );
384              
385             # then get parameters and types
386 0           foreach my $parameter (@params)
387             {
388 0           my ($type, $name) = split(" ",$parameter);
389              
390 0           $type =~ s/\s//g;
391 0           $name =~ s/\s//g;
392              
393 0           $parameters[$pc] = {
394             name => $name,
395             type => $type,
396             };
397 0           $pc++;
398             }
399              
400 0           $subroutine{param} = \@parameters;
401 0           $Class->add_operation(\%subroutine);
402             }
403             else
404             {
405 0           my @attributes = ();
406             # else next character is , or ;
407             # the line's a variable declaration
408 0           $Class->add_attribute ({
409             name => $name,
410             type => $type,
411             visibility => $self->{visibility},
412             });
413 0           my %attribute = { name => $name , type => $type };
414 0           $attributes[0] = \%attribute;
415 0 0         if ($rest =~ m/^\,.*\;/)
416             {
417 0           my @atts = split (",");
418 0           foreach my $attribute (@atts)
419             {
420 0           my @attribute_parts = split(" ", $attribute);
421 0           my $n = scalar @attribute_parts;
422 0           my $name = $attribute_parts[$n];
423 0           my $type = join(" ",$attribute_parts[0...$n-1]);
424 0           $Class->add_attribute ( {
425             name => $name,
426             type => $type,
427             visibility => $self->{visibility},
428             });
429             #
430             }
431             #
432             }
433             #
434             }
435             #
436             }
437             #
438             }
439             #
440             }
441             }
442             }
443              
444 0           $self->{Diagram} = $Diagram;
445 0           close $fh;
446 0           return;
447             }
448              
449             sub _discard_line
450             {
451 0     0     my $self = shift;
452 0           my $line = shift;
453 0           my $discard = 0;
454              
455             SWITCH:
456             {
457 0 0         if ($line =~ m/^\s*$/) # if line is blank or white space discard
  0            
458             {
459 0           $discard = 1;
460 0           last SWITCH;
461             }
462              
463 0 0         if ($line =~ /^\s*\/\//) # if line is a comment discard
464             {
465 0           $discard = 1;
466 0           last SWITCH;
467             }
468              
469             # if line is a comment discard
470 0 0         if ($line =~ m!^\s*/\*.*\*/!)
471             {
472 0           $discard = 1;
473 0           last SWITCH;
474             }
475              
476             # if line starts with multiline comment syntax discard and set flag
477 0 0         if ($line =~ /^\s*\/\*/)
478             {
479 0           $self->{comment} = 1;
480 0           $discard = 1;
481 0           last SWITCH;
482             }
483              
484 0 0         if ($line =~ /^.*\*\/\s*$/)
485             {
486 0           $self->{comment} = 0;
487             }
488 0 0         if ($self->{comment} == 1) # if currently inside a multiline comment
489             {
490             # if line starts with comment end syntax then unflag and discard
491 0 0         if ($line =~ /^.*\*\/\s*$/)
492             {
493 0           $self->{comment} = 0;
494 0           $discard = 1;
495 0           last SWITCH;
496             }
497              
498 0           $discard = 1;
499 0           last SWITCH;
500             }
501             }
502 0           return $discard;
503             }
504              
505             ####-----
506              
507             sub _parse_private_things {
508 0     0     my $self = shift;
509 0           my $line = shift;
510 0           my $Class = shift;
511              
512 0 0         return unless ($line =~ m/^\s*private\s*\w*:\s*(\w.*)$/);
513             # print "found private/public things\n";
514 0           my @private_things = split(";",$1);
515 0           foreach my $private_thing (@private_things) {
516 0           print "- private/public thing : $private_thing\n";
517             # FIXME : Next line type definition seems erroneous. Any C++ hackers care to check it?
518 0           $private_thing =~ m/^\s*(public|private)?:?\s*(static|virtual)\s*(\w+\s*\*?)\s*(\w+\(?[\w\s]*\)?)\s*\w*\s*\w*.*$/;
519 0           my $name = $4;
520 0           my $type = "$2 $3";
521 0   0       my $vis = $1 || $self->{visibility};
522             # print "- found declaration : name = $name, type = $type\n";
523              
524 0 0         if ($name =~ /\(/) {
525             # print "-- declaration is a method \n";
526             # check for simple sub
527 0 0         if ($private_thing =~ m/^ # start of line
528             \s* # whitespace
529             (?:public|private)?:?\s*
530             (\w*?\s*?(\w+\s*::\s*)*\w*?\*?) # type of the method: $1
531             \s* # whitespace
532             (~?\w+) # name of the method: $2
533             \s* # whitespace
534             \(\s* # start of parameter list
535             ([:\w\,\s\*=&\"]*) # all parameters: $3
536             (\)?) # may be an ending bracket: $4
537             [\w\s=]*(;?) # possibly end of signature $5
538             .*$/x
539             ) {
540 0           my $name = $3;
541 0   0       my $type = $1 || "void";
542 0           my $params = $4;
543 0           my $end_bracket = $5;
544 0           my $end_semicolon = $6;
545              
546 0           my $have_continuation = 0;
547 0           my $have_end_semicolon= 1;
548              
549 0           $params =~ s|\s+$||;
550 0           my @params = split(",",$params);
551 0           my $pc = 0; # parameter count
552              
553 0           my %subroutine = (
554             name => $name,
555             type => $type,
556             visibility => $self->{visibility},
557             );
558              
559              
560             # then get parameters and types
561 0           my @parameters = ();
562             # print "All parameters: ",join(';',@params),"\n";
563 0           foreach my $parameter (@params) {
564 0           $parameter =~ s/const\s+//;
565              
566 0           my ($type, $name) = split(" ",$parameter);
567              
568 0           $type =~ s/\s//g;
569 0           $name =~ s/\s//g;
570              
571 0           $parameters[$pc] = {
572             name => $name,
573             type => $type,
574             };
575 0           $pc++;
576             }
577              
578 0           $subroutine{param} = \@parameters;
579 0           $Class->add_operation(\%subroutine);
580             }
581             } else {
582             # print "-- declaration is an attribute \n";
583 0           $Class->add_attribute({
584             name => $name,
585             visibility => $vis,
586             type => $type,
587             });
588             }
589             }
590 0           return;
591             }
592              
593             sub _is_package
594             {
595 0     0     my $self = shift;
596 0           my $package = shift;
597 0           my $Diagram = $self->{Diagram};
598              
599 0 0         unless(ref $$package)
600             {
601 0           my $filename = shift;
602             # create new class with name
603 0           $$package = Autodia::Diagram::Class->new($filename);
604             # add class to diagram
605 0           $Diagram->add_class($$package);
606             }
607              
608 0           return;
609             }
610              
611             ####-----
612              
613             1;
614              
615             ###############################################################################
616              
617             =head1 NAME
618              
619             Autodia::Handler::Cpp - AutoDia handler for C++
620              
621             =head1 INTRODUCTION
622              
623             This module 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.
624              
625             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.
626              
627             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:
628              
629             %language_handlers = { .. , cpp => "Autodia::Handler::Cpp", .. };
630              
631             =head1 CONSTRUCTION METHOD
632              
633             use Autodia::Handler::Cpp;
634              
635             my $handler = Autodia::Handler::Cpp->New(\%Config);
636              
637             This creates a new handler using the Configuration hash to provide rules selected at the command line.
638              
639             =head1 ACCESS METHODS
640              
641             This parses the named file and returns 1 if successful or 0 if the file could not be opened.
642              
643             $handler->output_xml(); # interpolates values into an xml or html template
644              
645             $handler->output_graphviz(); # generates a gif file via graphviz
646              
647             =cut