Back to home page

Project CMSSW displayed by LXR

 
 

    


Warning, /TrackingTools/TrackAssociator/tools/classdiagram is written in an unsupported language. File is not indexed.

0001 #!/bin/env perl
0002 #################################################################
0003 #
0004 #  File and Version Information:
0005 #  
0006 #  Environment:
0007 #        Software developed for the CMS Detector at CERN LHC
0008 #
0009 #  Author List:
0010 #        Dmytro Kovalskyi
0011 #
0012 #################################################################
0013 use warnings;
0014 use strict;
0015 use Data::Dumper;
0016 use Getopt::Long;
0017 
0018 ###########################################################
0019 ## Default setings
0020 ###########################################################
0021 my $cmssw_base = $ENV{CMSSW_BASE};
0022 my $cmssw_release_base = $ENV{CMSSW_RELEASE_BASE};
0023 my ($debug,$className,$fileName,$show,$verbose,$glimpse,@fileNames,@classNames);
0024 my $outputFile = "diagram.violet";
0025 my $violet = "/afs/cern.ch/user/d/dmytro/public/files/violet-0.15.jar";
0026 my $dX = 300;
0027 my $dY = 200;
0028 my $X0 = 30;
0029 my $Y0 = 30;
0030 my $cmsglimpse = "cmsglimpse";
0031 
0032 # structure
0033 my %classTree = ();
0034 # each class is:
0035 #   perents [],
0036 #   new_public_methods {},
0037 #   other_public_methods {},
0038 #   variables [],
0039 #   files []
0040 
0041 sub help{
0042         system("perldoc $0");
0043         exit;
0044     }
0045 
0046 help() if (@ARGV==0);                                                                                                                                                          
0047 GetOptions("help|h"      => sub{ help() }
0048     , "debug|d" => \$debug
0049     , "output|o=s" => \$outputFile
0050     , "verbose|v" => \$verbose
0051     , "class|c=s" => \$className
0052     , "file|f=s" => \$fileName
0053     , "show|s" => \$show
0054     , "editor|e=s" => \$violet
0055     , "glimpse|g" => \$glimpse
0056 )
0057     or help();
0058 
0059 ###########################################################
0060 
0061 die "Class name is not provided. Abort" if (!defined $className);
0062 @classNames = split(/\s+/,$className);
0063 if (!defined $fileName){
0064     if (defined $glimpse){
0065         print "Searching for class information with glimpse...\n";
0066         foreach my $class (@classNames){
0067             my $query = 'class[^a-zA-Z0-9_]*'.($class).'[^\;]';
0068             my $glimpseReport = "";
0069             if (length($query)>30){
0070                 my $shortQuery = $query; $shortQuery =~ s/$class.*$/$class/;
0071                 $shortQuery = substr($shortQuery,1,30);
0072                 $glimpseReport = `$cmsglimpse \'$shortQuery\'|grep -E \'$query\'`;
0073             }else{
0074                 $glimpseReport = `$cmsglimpse \'$query\'`;
0075             }
0076             print "\n\n$glimpseReport\n\n" if (defined $debug);
0077             my %files = ();
0078             foreach my $line(split(/\n/,$glimpseReport)){
0079                 if($line =~ /^(\S+):(.*)/){
0080                     $files{$1} .= "$2\n";
0081                 }
0082             }
0083             die "Glimpse failed to find any source file for class $class. Try to specify the file where the class is declared explicitly\n"
0084               if(scalar keys %files == 0);
0085             push @fileNames, keys %files;
0086         }
0087         print "Done.\n";
0088     }else{
0089         die "Source file name is not provided. Abort\n";
0090     }
0091 }else{
0092     @fileNames = ($fileName);
0093 }
0094 
0095 sub parents{
0096     my @orderedClasses = @_;
0097     my @allParents = ();
0098     # die scalar @{$orderedClasses[0]},"\n";
0099     # print Dumper @{$list};
0100     foreach my $class( @{$orderedClasses[0]} ){
0101         push @allParents, @{$classTree{$class}->[0]};
0102     }
0103     my %parents = ();
0104     foreach my $parent(@allParents){
0105         if (! defined findClassInOrderList(\@orderedClasses,$parent)){
0106             $parents{$parent}++;
0107         }
0108     }
0109     print "Unique parents of ",join(" ",@{$orderedClasses[0]}),":\n", join("\n",keys %parents),"\n" if (defined $debug);
0110     # print Dumper @orderedClasses;
0111     return keys %parents;
0112 }
0113 
0114 sub findClassInOrderList{
0115     my ($list,$className) = @_;
0116     my $iX = 0;
0117     foreach my $x(@{$list}){
0118         my $iY = 0;
0119         foreach my $class(@{$x}){
0120             return ($iX,$iY) if ($class eq $className);
0121             $iY++;
0122         }
0123         $iX++;
0124     }
0125     return undef;
0126 }
0127 
0128 sub getXmlCompliantText{
0129     my $text = shift;
0130     $text =~ s/\</&lt;/g;
0131     $text =~ s/\>/&gt;/g;
0132     $text =~ s/\&/&amp;/g;
0133 #    $text =~ s/\=/&eq;/g;
0134     $text =~ s/\//&slash;/g;
0135     return $text;
0136 }
0137 
0138 sub print_uml{
0139     open(OUT,">$outputFile")||die "Cannot open file $outputFile\n$!\n";
0140        print OUT <<"EOF";
0141 <?xml version="1.0" encoding="UTF-8"?>
0142 <java version="1.5.0_06" class="java.beans.XMLDecoder">
0143  <object class="com.horstmann.violet.ClassDiagramGraph">      
0144 EOF
0145 
0146     my $x = $X0;
0147     my $y = $Y0;
0148     
0149     # sort classes for presentation
0150     my @orderedClasses = (\@classNames);
0151 
0152     while (scalar parents(@orderedClasses) > 0){
0153         my @parents = parents(@orderedClasses);
0154         unshift @orderedClasses, \@parents;
0155     }
0156     
0157     my $connections = "";
0158     foreach my $classSet (@orderedClasses){
0159         $x = $X0;
0160         foreach my $class (@{$classSet}){
0161             print OUT '<void method="addNode"><object id="'.$class.'" ';
0162             print OUT 'class="com.horstmann.violet.ClassNode">',"\n";
0163             print OUT '<void property="attributes"><void property="text"><string>';
0164             foreach my $var(@{$classTree{$class}->[3]}){
0165                 print OUT getXmlCompliantText("$var\n");
0166             }
0167             print OUT "</string></void></void>\n";
0168             print OUT '<void property="methods"><void property="text"><string>';
0169             foreach my $method(sort keys %{$classTree{$class}->[2]}){
0170                 print OUT getXmlCompliantText("$method()\n");
0171             }
0172             print OUT "</string></void></void>\n";
0173             print OUT '<void property="name"><void property="text"><string>'.($class).'</string></void></void></object>';
0174             print OUT '<object class="java.awt.geom.Point2D$Double"><void method="setLocation"><double>'.($x).'</double>';
0175             print OUT '<double>'.($y)."</double></void></object></void>\n";
0176             $x+=$dX;
0177             foreach my $parent(@{$classTree{$class}->[0]}){
0178                 $connections .= '<void method="connect"><object class="com.horstmann.violet.ClassRelationshipEdge">';
0179                 # $connections .= '<void property="bentStyle"><object class="com.horstmann.violet.BentStyle" field="VH"/></void>';
0180                 $connections .= '<void property="endArrowHead"><object class="com.horstmann.violet.ArrowHead" field="TRIANGLE"/>';
0181                 $connections .= '</void></object><object idref="'.($class).'"/><object idref="'.($parent).'"/></void>'."\n";
0182             }
0183         }
0184         $y+=$dY;
0185     }
0186     print OUT "$connections\n</object></java>\n";
0187     close OUT;
0188 }
0189 
0190 sub get_parents{
0191     my ($inheritance) = @_;
0192     my @parents = ();
0193     my @classes = split(/\,/,$inheritance);
0194     my $public_inheritance = 0;
0195     foreach my $class (@classes){
0196         print "debug: $class\n" if (defined $debug);
0197         $class =~ s/virtual//gs;
0198         if ($class =~ /^\s*public\s/i){
0199             $class =~ s/^\s*public\s+//i;
0200             $public_inheritance = 1;
0201         }
0202         if ($class =~ /^\s*private\s/i){
0203             $class =~ s/^\s*private\s+//i;
0204             $public_inheritance = 0;
0205         }
0206         if ($class =~ /^\s*protected\s/i){
0207             $class =~ s/^\s*protected\s+//i;
0208             $public_inheritance = 0;
0209         }
0210         if ($public_inheritance && $class =~ /^\s*(\S+)\s*$/){
0211             my $className = $1;
0212             # strip out templates
0213             $className =~ s/\<.*?\>//g;
0214             # get rid of namespace
0215             $className =~ s/\S+:://;
0216             push @parents,$className;
0217         }
0218         print "debug: $class\n" if(defined $debug);
0219     }
0220     return @parents;
0221 }
0222 
0223 sub get_details{
0224     my $source = shift;
0225     my $className = shift;
0226     my %methods = ();
0227     my @data = ();
0228     # first get rid of blocks in {}
0229     # to do this for implementations we need to repeat a few times
0230     # print "debug:\n$source\n" if (defined $debug);
0231     while ($source =~ s/\{[^\}\{]*\}/;/gs){}
0232     $source =~ s/\}.*//gs;
0233     
0234     # get rid of method arguments
0235     $source =~ s/\([^\)]+\)/\(\)/gs;
0236     
0237     # get rid of compiler commands
0238     $source =~ s/\#.*$//mg;
0239 
0240     print "debug:\n$source\n" if (defined $debug);
0241     
0242     
0243     # # get rid of constructors
0244     # $source =~ s/$className\s*\(\).*?\;//sg;
0245         
0246     # # get rif of destructor traces
0247     # $source =~ s/~//sg;
0248 
0249     $source =~ s/\n\s*\n/\n/sg;
0250     
0251     my @blocks = split(/;/,$source);
0252     my $public_block = 0;
0253     foreach my $block(@blocks){
0254         $public_block = 1 if ($block =~ s/^\s*public\s*://i);
0255         $public_block = 0 if ($block =~ s/^\s*private\s*://i);
0256         $public_block = 0 if ($block =~ s/^\s*protected\s*://i);
0257         $block =~ s/\~+//;
0258           
0259         print "### debug: $block\n" if (defined $debug);
0260         
0261         if ($block =~ /([^\s\:]+)\s*\(\)/s){
0262             my $match = $1;
0263             # found method
0264             $match =~ s/^\*//;
0265             $match =~ s/^\&//;
0266             next if($match eq $className);
0267             $methods{$match}++ if ($public_block); # ignore non-public methods.
0268         }else{
0269             next if ( ($block =~ /^\s*enum /)
0270                 || ($block =~ /^\s*class /)
0271                 || ($block =~ /^\s*struct /)
0272                 || ($block =~ /^\s*template/)
0273                 || ($block =~ /^\s*friend\s+class /)
0274 #               || ($block =~ /^\s*const /)
0275                 || ($block =~ /^\s*typedef /)
0276                 || ($block =~ /^\s*using /));
0277             $block =~ s/\n/ /g;
0278             $block =~ s/\s+/ /g;
0279             
0280             push @data, $block if($block =~ /\S/);
0281         }
0282     }
0283 
0284     return [\%methods,\@data];
0285 }
0286 
0287 sub find_class{
0288     my ($fileNames, $className) = @_;
0289     foreach my $fileName (@{$fileNames}){
0290         my $file = $fileName;
0291         $file = "$cmssw_base/src/$fileName" if (! -e $file);
0292         $file = "$cmssw_release_base/src/$fileName" if (! -e $file);
0293         die "Cannot find file $fileName. Abort\n" if (! -e $file);
0294         
0295         # search for class defenition
0296         my @parents = ();
0297         my $source = `cat $file`;
0298         print "processing file $file ...\n" if (defined $debug);
0299         # get rid of comments
0300         $source =~ s/\/\*.*?\*\///gs;
0301         $source =~ s/\/\/.*$//mg;
0302         my $full_source = $source;
0303 #       if ($source =~ s/.*?class\s+$className([^\w\s\_\{]|\s*)([^\;\(\)\{]*?)\{//is){
0304         if ($source =~ s/.*?class\s+$className([^\w\_\{\;\(\)][^\;\(\)\{]*\{|\s*\{)//is){
0305             print "debug:\n$source\n" if (defined $debug);
0306             my $inheritance = "$1";
0307             $inheritance =~ s/.*?://; $inheritance =~ s/\n//g; $inheritance =~ s/\s*\{//;
0308             @parents = get_parents($inheritance);
0309             if(defined $debug){
0310                 print "class $className inherits from\n";
0311                 foreach my $parent(@parents){
0312                     print "\t$parent\n";
0313                 }
0314             }
0315             
0316             ## search for public methods and data.
0317             
0318             my $details = get_details($source,$className);
0319             my %methods = %{$details->[0]};
0320             my @data = @{$details->[1]};
0321             if (defined $debug){
0322                 print "public methods:\n";
0323                 foreach my $method (sort keys %methods){
0324                     print "\t$method()\n";
0325                 }
0326                 print "data:\n";
0327                 foreach my $var (@data){
0328                     print "\t$var\n";
0329                 }
0330             }
0331             
0332             my @fileList = ();
0333             # collect include files information if there are any parents.
0334             if (scalar @parents > 0){
0335                 # @fileList = ($full_source =~ /\#include\s*[\"\<]([^\"\>]+)[\"\<]/gm);
0336                 @fileList = ($full_source =~ /^\#include\s*\W([A-Z][^\"\>\<]+.h)/gm);
0337                 push @fileList, $fileName;
0338                 if (defined $debug){
0339                     print "Include files:\n";
0340                     foreach my $file(@fileList){
0341                         print "\t$file\n";
0342                     }
0343                 }
0344             }
0345             return [\@parents,{},\%methods,\@data,\@fileList];
0346         }
0347     }
0348     if (defined $debug){
0349         print "Cannot find class $className among files:\n";
0350         foreach my $file(@{$fileNames}){
0351             print "\t$file\n";
0352         }
0353     }
0354     return undef;
0355 }
0356 
0357 # name of classes that has to be analyzed
0358 # along with files that can contain information about these classes
0359 my @classesToProcess = ();
0360 foreach my $class(@classNames){
0361     unshift @classesToProcess, [\@fileNames,$class];
0362 }
0363 while (scalar @classesToProcess > 0){
0364     my $classInfo = find_class(@{$classesToProcess[0]});
0365     print "Cannot find class $classesToProcess[0]->[1]\n" if (! defined $classInfo);
0366     # TODO: sort includes by a resonable chance to find a class defenition
0367     
0368     $classTree{$classesToProcess[0]->[1]} = $classInfo;
0369     shift  @classesToProcess;
0370     
0371     # check that all classes are known
0372     foreach my $parent (@{$classInfo->[0]}){
0373         unshift @classesToProcess, [$classInfo->[4],$parent] if (! defined $classTree{$parent});
0374     }
0375     print "number of classes to analyze: ", scalar @classesToProcess, "\n\n" if (defined $debug);
0376 } 
0377 print_uml();
0378 
0379 if (defined $show){
0380     if (! -e "$violet"){
0381         print "Cannot find Violet editor: $violet. Exit.\n";
0382     }else{
0383         system("java -jar $violet $outputFile&");
0384     }
0385 }
0386 
0387 exit;
0388 
0389 =head1 NAME
0390 
0391 classdiagram - a tool to analyze inheritance structure of C++ classes in CMSSW environment
0392   
0393 =head1 SYNOPSYS
0394 
0395 classdiagram [-h|--help] [-c|--class <class name>] [-f|--file <interface file>] [-s|--show] [-o|--output <output file>] [-d|--debug] [-e|--editor <jar file>] [-g||--glimpse]
0396 
0397 =head1 OPTIONS
0398 
0399 =over
0400 
0401 =item -c, --class <class name>
0402 
0403 Name of the class to analyze.
0404 
0405 =item -f, --file <file>
0406 
0407 Name of the file that contains declaration of the class.
0408 
0409 =item -s, --show
0410 
0411 Open the output file with Violet.
0412 
0413 =item -o, --output <file>
0414 
0415 Output file name to store the XML version of the class diagram.
0416 
0417 =item -e, --editor <jar file>
0418 
0419 Specify the jar file of Violet editor location
0420 
0421 =item -g, --glimpse
0422 
0423 Use glimpse to find file where the class is defined. In this case you don't need to provide the file name explicitly.
0424 
0425 =item -h, --help
0426 
0427 =back
0428 
0429 =head1 DESCRIPTION
0430 
0431 classdiagram is a simple tool to analyze a class structure in CMSSW environment. Its main
0432 goal is to provide a quick view of the most important aspects of a class hierarchy in the
0433 form of UML class diagram. The standard CMSSW code browsing and search tools, such as 
0434 Doxygen and LXR provide all necessary information that one needs to understand the class 
0435 structure, but unfortunately quite often amount of details can be overwhelming to get an
0436 idea of how classes are organized quickly.
0437   
0438 The program is based on a assumption that in most cases it is sufficient to know class
0439 data members and public methods to get an idea of what this class can and cannot do.
0440 Stripping out the rest one can get a compact and easy to read class diagram, which
0441 normally fits in the working window. It is also assumed that all public methods and 
0442 data members have meaningful names, otherwise it is close to impossible to get a 
0443 quick overview of the class structure without reading and understanding the source code
0444 and/or class documentation.
0445   
0446 The output of the program is an XML data file in the format of Violet UML editor
0447 developed by Cay Horstmann, which is a free Java based graphical editor with an 
0448 intuitive interface. http://horstmann.com/violet/. The editor allows you to modify, 
0449 save and even export as a gif image the class diagram.
0450   
0451 classdiagram takes as input a class name and a header file where the interface 
0452 is defined. It parses the code looking for base class information in the include
0453 files. A typical way to use it:
0454 
0455 =over
0456 
0457 classdiagram -f Geometry/Surface/interface/BoundPlane.h -c BoundPlane
0458 
0459 =back
0460 
0461 or
0462 
0463 =over
0464 
0465 classdiagram -f Geometry/Surface/interface/BoundPlane.h -c BoundPlane -s
0466 
0467 =back
0468 
0469 In the first example the class diagram is built for class BoundPlane and stored
0470 in an XML format in diagram.violet file. The second example also starts Violet 
0471 editor which to show the class diagram. 
0472   
0473 WARNING: since distributing properly classes in the class diagram can be 
0474 non-trivial task, it is expected that a user will move classes around to make
0475 the diagram look the he/she likes. No information is lost or changed in such 
0476 manipulations.
0477   
0478 =head1 AUTHOR
0479                                                                                                                                                          
0480 Dmytro Kovalskyi, Unversity of California
0481                                                                                                                                                            
0482 dmytro@slac.standford.edu
0483 
0484 Santa Barbar, 2006
0485                                                                                                                                                            
0486 =cut