package Codecheck::Libraries::NamingConventions; use strict; use Exporter; our @ISA = qw(Exporter); our @EXPORT = qw(setOptions registerNamingErrors getNamingDescription setNamingOptions checkNamingRules getPrimaryKind NAMEERROR); our $kind; #The display name for the tested entity kind our $kindPlural; # The display name for multiple entity, e.g. classes for class our $languageText; # The language name how it should be displayed our $languageTest; # The language name how it needs to appear when testing for compliance our $canChangePrecedence; # Set to 1 if the entity kind has conflicting kinds that need a precedence our $defaultAll; # Set to 1 to default to displaying an All entites option first our %kindNameLookup; # The pretty name for each entity kind, this defaults the base, e.g. Global Variable our @kindStrings; # List the individual kinds for the entity ############################################################### use constant NAMEERROR => '%1 name \'%2\' is invalid. %3'; use constant TOOSHORT => ' It is too short.'; use constant TOOLONG => ' It is too long.'; use constant MISSINGPREFIX => ' It is missing the required prefix.'; use constant BADPREFIX => ' It uses a disallowed prefix.'; use constant MISSINGSUFFIX => ' It is missing the required suffix.'; use constant BADSUFFIX => ' It uses a disallowed suffix.'; use constant MISSINGINITCAP=> ' It is missing the initial capital.'; use constant MISSINGINITLC=> ' It has a leading capital.'; use constant HASLOWERCASE=> ' It has lowercase letters.'; use constant HASUPPERCASE=> ' It has uppercase letters.'; use constant CONSECCAPS=>' It has consecutive capitals.'; use constant NONALPHANUM=>' It has characters other than letters and numbers.'; use constant NONALPHA=> ' It has characters other than letters.'; sub setOptions{ ($kind,$kindPlural,$languageText,$languageTest,$canChangePrecedence,$defaultAll) = @_; #setup default display names foreach (@kindStrings){ if(! $kindNameLookup{$_}){ $kindNameLookup{$_} = "$_ $kind"; } } $kindNameLookup{$kind}=$kind if $defaultAll; } sub registerNamingErrors{ my $check = shift; $check->add_tr_text(NAMEERROR); $check->add_tr_text(TOOSHORT); $check->add_tr_text(TOOLONG); $check->add_tr_text(MISSINGPREFIX); $check->add_tr_text(BADPREFIX); $check->add_tr_text(MISSINGSUFFIX); $check->add_tr_text(BADSUFFIX); $check->add_tr_text(MISSINGINITCAP); $check->add_tr_text(MISSINGINITLC); $check->add_tr_text(HASLOWERCASE); $check->add_tr_text(HASUPPERCASE); $check->add_tr_text(CONSECCAPS); $check->add_tr_text(NONALPHANUM); $check->add_tr_text(NONALPHA); foreach (values(%kindNameLookup)){ $check->add_tr_text($_); } } sub getNamingDescription{ my ($kind1,$kind2); if ($canChangePrecedence && @kindStrings>2){ $kind1 = $kindStrings[0]; $kind2 = $kindStrings[1]; } my $o; $o .= "Select the required naming convention for $languageText $kindPlural."; $o .= "
$kindPlural can have different naming conventions based off of their kind (@kindStrings)." if @kindStrings>1; $o .= "
The first group of options will define the naming conventions for All $kindPlural, the other options will let you specify a subset of $kindPlural to test." if $defaultAll; $o .= "
"; $o .= "Naming Convention Options:"; return $o; } sub setNamingOptions{ my $check = shift; if ($defaultAll){ setindividualOptions($check,$kind,scalar(@kindStrings)+1,1); } my $precCount = 1; foreach my $itemKind (@kindStrings){ setindividualOptions($check,$itemKind,$precCount); $precCount++; } } sub setindividualOptions{ my ($check, $itemKind,$precCount, $testAll) = @_; my $name; my $precedenceTotal = scalar @kindStrings; if ($testAll && $defaultAll){ $name = "All $kind"; }else{ $name = $kindNameLookup{$itemKind}; } if ($defaultAll){ $precedenceTotal++; } my $runValue = $precedenceTotal>1?0:1; $runValue = 1 if $testAll; my $space = " "; $check->option->checkbox($itemKind."Run", "Test ${name} names",$runValue); $check->option->choice($itemKind."Precedence",$space."Precedence",[1..$precedenceTotal],$precCount) if $precedenceTotal>1 && $canChangePrecedence; $check->option->integer($itemKind."minLength", $space."Minimum Length:",1); $check->option->integer($itemKind."maxLength", $space."Maximum Length:"); $check->option->text($itemKind."prefix", $space."Required Prefix:"); $check->option->text($itemKind."suffix", $space."Required Suffix:"); $check->option->choice($itemKind."charSet", $space."Character Set",["All Characters","Letters and Numbers Only","Letters Only"],"All Characters"); $check->option->choice( $itemKind."capOpt", $space."Capitalization:",["ignore","UpperCamelCase","lowerCamelCase","ALLCAPS","nocaps"],"ignore"); $check->option->choice($itemKind."consCaps", $space."Consecutive Capitals",["Allowed","Not Allowed"],"Allowed"); } sub checkNamingRules{ my ($name,$type,$check) = @_; my $errors; my $len = length($name); my $minLength = $check->option->lookup($type."minLength"); my $maxLength = $check->option->lookup($type."maxLength"); my @prefixList = split(/,/,$check->option->lookup($type."prefix")); my @suffixList = split(/,/,$check->option->lookup($type."suffix")); my $capOpt = $check->option->lookup($type."capOpt"); my $charSet = $check->option->lookup($type."charSet"); my $allowConsCaps = $check->option->lookup($type."consCaps") =~ /^Allowed$/; my $runTest = $check->option->lookup($type."Run"); my $subName = $name; return unless $runTest; if($maxLength>0 && $len > $maxLength){ $errors = TOOLONG; } if($len < $minLength){ $errors .= TOOSHORT; } #Process Prefix and Suffix, don't use for the rest of the tests if (@prefixList || @suffixList){ my $prefixUsed = 0; my $badPrefix = 0; PREFIX:foreach my $prefix( @prefixList){ $prefix =~ s/^\s+|\s+$//; if ($prefix =~ s/^[!]//){ $badPrefix=1; } if ($subName =~ /^$prefix(.*)/){ $prefixUsed =1; $subName = $1; last PREFIX; } } if ($prefixUsed && $badPrefix){ $errors .= BADPREFIX; } if (@prefixList && !$prefixUsed && ! $badPrefix){ $errors .= MISSINGPREFIX; } my $suffixUsed = 0; my $badSuffix = 0; SUFFIX:foreach my $suffix( @suffixList){ $suffix =~ s/^\s+|\s+$//; if ($suffix =~ s/^[!]//){ $badSuffix=1; } if ($subName =~ /(.*)$suffix$/){ $suffixUsed =1; $subName = $1; last SUFFIX; } } if ($suffixUsed && $badSuffix){ $errors .= BADSUFFIX; } if (@suffixList && !$suffixUsed && ! $badSuffix){ $errors .= MISSINGSUFFIX; } } #Check Case if ($capOpt =~ /UpperCamelCase/ && $subName !~ /^[A-Z]/){ $errors .= MISSINGINITCAP; } if ($capOpt =~ /lowerCamelCase/ && $subName !~ /^[a-z]/){ $errors .= MISSINGINITLC; } if ($capOpt =~ /ALLCAPS/ && $subName =~ /[a-z]/){ $errors .= HASLOWERCASE; } if ($capOpt =~ /nocaps/ && $subName =~ /[A-Z]/){ $errors .= HASUPPERCASE; } if(! $allowConsCaps && $subName =~ /[A-Z][A-Z]/){ $errors .= CONSECCAPS; } #Check character set if($charSet =~ /Letters and Numbers Only/ && $subName !~ /^[A-Za-z0-9]+$/){ $errors .= NONALPHANUM; } if($charSet =~ /Letters Only/ && $subName !~ /^[A-Za-z]+$/){ $errors .= NONALPHA; } return $errors; } sub getPrimaryKind{ my $check = shift; my $kindList = shift; my @varKinds; #Discard invalid kinds (those not in the testing list) foreach my $kind (split(/ /,$kindList)){ push(@varKinds, $kind) if $check->option->lookup($kind."Run"); } #Sort the valid kind list based off the precedence option and get the first kind @varKinds = sort{$check->option->lookup($a."Precedence") <=> $check->option->lookup($b."Precedence");} @varKinds; return $varKinds[0] if @varKinds; return $kind if $defaultAll; } 1;