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/\</</g;
0131 $text =~ s/\>/>/g;
0132 $text =~ s/\&/&/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