);
close(FILE);
$Content=~s/\r//g;
return $Content;
}
sub appendFile($$)
{
my ($Path, $Content) = @_;
return if(not $Path);
if(my $Dir = get_dirname($Path)) {
mkpath($Dir);
}
open(FILE, ">>".$Path) || die ("can't open file \'$Path\': $!\n");
print FILE $Content;
close(FILE);
}
sub get_Report_Header($)
{
my $Level = $_[0];
my $Report_Header = "";
if($Level eq "Source") {
$Report_Header .= "Source compatibility";
}
elsif($Level eq "Binary") {
$Report_Header .= "Binary compatibility";
}
else {
$Report_Header .= "API compatibility";
}
$Report_Header .= " report for the $TargetTitle library between ".$Descriptor{1}{"Version"}." and ".$Descriptor{2}{"Version"}." versions";
if($ClientPath) {
$Report_Header .= " (concerning portability of the client: ".get_filename($ClientPath).")";
}
$Report_Header .= "
\n";
return $Report_Header;
}
sub get_SourceInfo()
{
my $CheckedArchives = "";
if($OldStyle) {
$CheckedArchives .= "Java ARchives (".keys(%{$LibArchives{1}}).")
";
}
else {
$CheckedArchives .= "Java ARchives ".keys(%{$LibArchives{1}})."
";
}
$CheckedArchives .= "\n
\n";
foreach my $ArchivePath (sort {lc($a) cmp lc($b)} keys(%{$LibArchives{1}})) {
$CheckedArchives .= get_filename($ArchivePath)."
\n";
}
$CheckedArchives .= "
$TOP_REF
\n";
return $CheckedArchives;
}
sub get_TypeProblems_Count($$)
{
my ($TargetSeverity, $Level) = @_;
my $Type_Problems_Count = 0;
foreach my $Type_Name (sort keys(%{$TypeChanges{$Level}}))
{
my %Kinds_Target = ();
foreach my $Kind (sort keys(%{$TypeChanges{$Level}{$Type_Name}}))
{
if($TypeProblems_Kind{$Level}{$Kind} ne $TargetSeverity) {
next;
}
foreach my $Location (sort keys(%{$TypeChanges{$Level}{$Type_Name}{$Kind}}))
{
my $Target = $TypeChanges{$Level}{$Type_Name}{$Kind}{$Location}{"Target"};
if($Kinds_Target{$Kind}{$Target}) {
next;
}
$Kinds_Target{$Kind}{$Target} = 1;
$Type_Problems_Count += 1;
}
}
}
return $Type_Problems_Count;
}
sub show_number($)
{
if($_[0])
{
my $Num = cut_off_number($_[0], 2, 0);
if($Num eq "0")
{
foreach my $P (3 .. 7)
{
$Num = cut_off_number($_[0], $P, 1);
if($Num ne "0") {
last;
}
}
}
if($Num eq "0") {
$Num = $_[0];
}
return $Num;
}
return $_[0];
}
sub cut_off_number($$$)
{
my ($num, $digs_to_cut, $z) = @_;
if($num!~/\./)
{
$num .= ".";
foreach (1 .. $digs_to_cut-1) {
$num .= "0";
}
}
elsif($num=~/\.(.+)\Z/ and length($1)<$digs_to_cut-1)
{
foreach (1 .. $digs_to_cut - 1 - length($1)) {
$num .= "0";
}
}
elsif($num=~/\d+\.(\d){$digs_to_cut,}/) {
$num=sprintf("%.".($digs_to_cut-1)."f", $num);
}
$num=~s/\.[0]+\Z//g;
if($z) {
$num=~s/(\.[1-9]+)[0]+\Z/$1/g;
}
return $num;
}
sub get_Summary($)
{
my $Level = $_[0];
my ($Added, $Removed, $M_Problems_High, $M_Problems_Medium, $M_Problems_Low,
$T_Problems_High, $T_Problems_Medium, $T_Problems_Low, $M_Other, $T_Other) = (0,0,0,0,0,0,0,0,0,0);
%{$RESULT{$Level}} = (
"Problems"=>0,
"Warnings"=>0,
"Affected"=>0 );
foreach my $Method (sort keys(%CompatProblems))
{
foreach my $Kind (sort keys(%{$CompatProblems{$Method}}))
{
if(my $Severity = $MethodProblems_Kind{$Level}{$Kind})
{
foreach my $Location (sort keys(%{$CompatProblems{$Method}{$Kind}}))
{
if($Kind eq "Added_Method")
{
if($Level eq "Source")
{
if($ChangedReturnFromVoid{$Method}) {
next;
}
}
$Added+=1;
}
elsif($Kind eq "Removed_Method")
{
if($Level eq "Source")
{
if($ChangedReturnFromVoid{$Method}) {
next;
}
}
$Removed+=1;
$TotalAffected{$Level}{$Method} = $Severity;
}
else
{
if($Severity eq "Safe") {
$M_Other += 1;
}
elsif($Severity eq "High") {
$M_Problems_High+=1;
}
elsif($Severity eq "Medium") {
$M_Problems_Medium+=1;
}
elsif($Severity eq "Low") {
$M_Problems_Low+=1;
}
if(($Severity ne "Low" or $StrictCompat)
and $Severity ne "Safe") {
$TotalAffected{$Level}{$Method} = $Severity;
}
}
}
}
}
}
my %MethodTypeIndex = ();
foreach my $Method (sort keys(%CompatProblems))
{
my @Kinds = sort keys(%{$CompatProblems{$Method}});
foreach my $Kind (@Kinds)
{
if(my $Severity = $TypeProblems_Kind{$Level}{$Kind})
{
my @Locs = sort {length($a)<=>length($b)} sort keys(%{$CompatProblems{$Method}{$Kind}});
foreach my $Location (@Locs)
{
my $Type_Name = $CompatProblems{$Method}{$Kind}{$Location}{"Type_Name"};
my $Target = $CompatProblems{$Method}{$Kind}{$Location}{"Target"};
if(defined $MethodTypeIndex{$Method}{$Type_Name}{$Kind}{$Target})
{ # one location for one type and target
next;
}
$MethodTypeIndex{$Method}{$Type_Name}{$Kind}{$Target} = 1;
$TypeChanges{$Level}{$Type_Name}{$Kind}{$Location} = $CompatProblems{$Method}{$Kind}{$Location};
if(($Severity ne "Low" or $StrictCompat)
and $Severity ne "Safe")
{
if(my $Sev = $TotalAffected{$Level}{$Method})
{
if($Severity_Val{$Severity}>$Severity_Val{$Sev}) {
$TotalAffected{$Level}{$Method} = $Severity;
}
}
else {
$TotalAffected{$Level}{$Method} = $Severity;
}
}
}
}
}
}
%MethodTypeIndex = (); # clear memory
$T_Problems_High = get_TypeProblems_Count("High", $Level);
$T_Problems_Medium = get_TypeProblems_Count("Medium", $Level);
$T_Problems_Low = get_TypeProblems_Count("Low", $Level);
$T_Other = get_TypeProblems_Count("Safe", $Level);
my $SCount = keys(%CheckedMethods)-$Added;
if($SCount)
{
my %Weight = (
"High" => 100,
"Medium" => 50,
"Low" => 25
);
foreach (keys(%{$TotalAffected{$Level}})) {
$RESULT{$Level}{"Affected"}+=$Weight{$TotalAffected{$Level}{$_}};
}
$RESULT{$Level}{"Affected"} = $RESULT{$Level}{"Affected"}/$SCount;
}
else {
$RESULT{$Level}{"Affected"} = 0;
}
$RESULT{$Level}{"Affected"} = show_number($RESULT{$Level}{"Affected"});
if($RESULT{$Level}{"Affected"}>=100) {
$RESULT{$Level}{"Affected"} = 100;
}
my ($TestInfo, $TestResults, $Problem_Summary) = ();
# test info
$TestInfo .= "Test Info
\n";
$TestInfo .= "\n";
$TestInfo .= "Library Name | $TargetTitle |
\n";
$TestInfo .= "Version #1 | ".$Descriptor{1}{"Version"}." |
\n";
$TestInfo .= "Version #2 | ".$Descriptor{2}{"Version"}." |
\n";
if($JoinReport)
{
if($Level eq "Binary") {
$TestInfo .= "Subject | Binary Compatibility |
\n"; # Run-time
}
if($Level eq "Source") {
$TestInfo .= "Subject | Source Compatibility |
\n"; # Build-time
}
}
$TestInfo .= "
\n";
# test results
$TestResults .= "Test Results
";
$TestResults .= "";
my $Checked_Archives_Link = "0";
$Checked_Archives_Link = "".keys(%{$LibArchives{1}})."" if(keys(%{$LibArchives{1}})>0);
$TestResults .= "Total JARs | $Checked_Archives_Link |
\n";
$TestResults .= "Total Methods / Classes | ".keys(%CheckedMethods)." / ".keys(%CheckedTypes)." |
\n";
$RESULT{$Level}{"Problems"} += $Removed+$M_Problems_High+$T_Problems_High+$T_Problems_Medium+$M_Problems_Medium;
if($StrictCompat) {
$RESULT{$Level}{"Problems"}+=$T_Problems_Low+$M_Problems_Low;
}
else {
$RESULT{$Level}{"Warnings"}+=$T_Problems_Low+$M_Problems_Low;
}
my $META_DATA = "kind:".lc($Level).";";
$META_DATA .= $RESULT{$Level}{"Problems"}?"verdict:incompatible;":"verdict:compatible;";
$TestResults .= "Compatibility | \n";
my $BC_Rate = show_number(100 - $RESULT{$Level}{"Affected"});
if($RESULT{$Level}{"Problems"})
{
my $Cl = "incompatible";
if($BC_Rate>=90) {
$Cl = "warning";
}
elsif($BC_Rate>=80) {
$Cl = "almost_compatible";
}
$TestResults .= "".$BC_Rate."% | \n";
}
else
{
$TestResults .= "100% | \n";
}
$TestResults .= "
\n";
$TestResults .= "
\n";
$META_DATA .= "affected:".$RESULT{$Level}{"Affected"}.";";# in percents
# Problem Summary
$Problem_Summary .= "Problem Summary
";
$Problem_Summary .= "";
$Problem_Summary .= " | Severity | Count |
";
my $Added_Link = "0";
if($Added>0)
{
if($ShortMode) {
$Added_Link = $Added;
}
else
{
if($JoinReport) {
$Added_Link = "$Added";
}
else {
$Added_Link = "$Added";
}
}
}
$META_DATA .= "added:$Added;";
$Problem_Summary .= "Added Methods | - | $Added_Link |
";
my $Removed_Link = "0";
if($Removed>0)
{
if($ShortMode) {
$Removed_Link = $Removed;
}
else
{
if($JoinReport) {
$Removed_Link = "$Removed"
}
else {
$Removed_Link = "$Removed"
}
}
}
$META_DATA .= "removed:$Removed;";
$Problem_Summary .= "Removed Methods | ";
$Problem_Summary .= "High | $Removed_Link |
";
my $TH_Link = "0";
$TH_Link = "$T_Problems_High" if($T_Problems_High>0);
$META_DATA .= "type_problems_high:$T_Problems_High;";
$Problem_Summary .= "Problems with Data Types | ";
$Problem_Summary .= "High | $TH_Link |
";
my $TM_Link = "0";
$TM_Link = "$T_Problems_Medium" if($T_Problems_Medium>0);
$META_DATA .= "type_problems_medium:$T_Problems_Medium;";
$Problem_Summary .= "Medium | $TM_Link |
";
my $TL_Link = "0";
$TL_Link = "$T_Problems_Low" if($T_Problems_Low>0);
$META_DATA .= "type_problems_low:$T_Problems_Low;";
$Problem_Summary .= "Low | $TL_Link |
";
my $MH_Link = "0";
$MH_Link = "$M_Problems_High" if($M_Problems_High>0);
$META_DATA .= "method_problems_high:$M_Problems_High;";
$Problem_Summary .= "Problems with Methods | ";
$Problem_Summary .= "High | $MH_Link |
";
my $MM_Link = "0";
$MM_Link = "$M_Problems_Medium" if($M_Problems_Medium>0);
$META_DATA .= "method_problems_medium:$M_Problems_Medium;";
$Problem_Summary .= "Medium | $MM_Link |
";
my $ML_Link = "0";
$ML_Link = "$M_Problems_Low" if($M_Problems_Low>0);
$META_DATA .= "method_problems_low:$M_Problems_Low;";
$Problem_Summary .= "Low | $ML_Link |
";
# Safe Changes
if($T_Other)
{
my $TS_Link = "$T_Other";
$Problem_Summary .= "Other Changes in Data Types | - | $TS_Link |
\n";
}
if($M_Other)
{
my $MS_Link = "$M_Other";
$Problem_Summary .= "Other Changes in Methods | - | $MS_Link |
\n";
}
$META_DATA .= "checked_methods:".keys(%CheckedMethods).";";
$META_DATA .= "checked_types:".keys(%CheckedTypes).";";
$META_DATA .= "tool_version:$TOOL_VERSION";
$Problem_Summary .= "
\n";
return ($TestInfo.$TestResults.$Problem_Summary, $META_DATA);
}
sub getStyle($$$)
{
my ($Subj, $Act, $Num) = @_;
my %Style = (
"Added"=>"new",
"Removed"=>"failed",
"Safe"=>"passed",
"Low"=>"warning",
"Medium"=>"failed",
"High"=>"failed"
);
if($Num>0) {
return " class='".$Style{$Act}."'";
}
return "";
}
sub get_Anchor($$$)
{
my ($Kind, $Level, $Severity) = @_;
if($JoinReport)
{
if($Severity eq "Safe") {
return "Other_".$Level."_Changes_In_".$Kind."s";
}
else {
return $Kind."_".$Level."_Problems_".$Severity;
}
}
else
{
if($Severity eq "Safe") {
return "Other_Changes_In_".$Kind."s";
}
else {
return $Kind."_Problems_".$Severity;
}
}
}
sub get_Report_Added($)
{
if($ShortMode) {
return "";
}
my $Level = $_[0];
my ($ADDED_METHODS, %MethodAddedInArchiveClass);
foreach my $Method (sort keys(%CompatProblems))
{
foreach my $Kind (sort keys(%{$CompatProblems{$Method}}))
{
if($Kind eq "Added_Method")
{
my $ArchiveName = $MethodInfo{2}{$Method}{"Archive"};
my $ClassName = get_ShortName($MethodInfo{2}{$Method}{"Class"}, 2);
if($Level eq "Source")
{
if($ChangedReturnFromVoid{$Method}) {
next;
}
}
$MethodAddedInArchiveClass{$ArchiveName}{$ClassName}{$Method} = 1;
}
}
}
my $Added_Number = 0;
foreach my $ArchiveName (sort {lc($a) cmp lc($b)} keys(%MethodAddedInArchiveClass))
{
foreach my $ClassName (sort {lc($a) cmp lc($b)} keys(%{$MethodAddedInArchiveClass{$ArchiveName}}))
{
my %NameSpace_Method = ();
foreach my $Method (keys(%{$MethodAddedInArchiveClass{$ArchiveName}{$ClassName}})) {
$NameSpace_Method{$MethodInfo{2}{$Method}{"Package"}}{$Method} = 1;
}
my $ShowClass = $ClassName;
$ShowClass=~s/<.*>//g;
foreach my $NameSpace (sort keys(%NameSpace_Method))
{
$ADDED_METHODS .= "$ArchiveName, ".htmlSpecChars($ShowClass).".class
\n";
if($NameSpace) {
$ADDED_METHODS .= "package $NameSpace
\n";
}
if($Compact) {
$ADDED_METHODS .= "";
}
my @SortedMethods = sort {lc($MethodInfo{2}{$a}{"Signature"}) cmp lc($MethodInfo{2}{$b}{"Signature"})} keys(%{$NameSpace_Method{$NameSpace}});
foreach my $Method (@SortedMethods)
{
$Added_Number += 1;
my $Signature = undef;
if($Compact) {
$Signature = get_Signature($Method, 2, "Full|HTML|Simple");
}
else {
$Signature = highLight_Signature_Italic_Color($Method, 2);
}
if($NameSpace) {
$Signature=~s/(\W|\A)\Q$NameSpace\E\.(\w)/$1$2/g;
}
if($Compact) {
$ADDED_METHODS .= " ".$Signature."
\n";
}
else {
$ADDED_METHODS .= insertIDs($ContentSpanStart.$Signature.$ContentSpanEnd."
\n".$ContentDivStart."[mangled: ".htmlSpecChars($Method)."]
".$ContentDivEnd."\n");
}
}
if($Compact) {
$ADDED_METHODS .= "
";
}
$ADDED_METHODS .= "
\n";
}
}
}
if($ADDED_METHODS)
{
my $Anchor = "";
if($JoinReport) {
$Anchor = "";
}
if($OldStyle) {
$ADDED_METHODS = "Added Methods ($Added_Number)
\n".$ADDED_METHODS;
}
else {
$ADDED_METHODS = "Added Methods $Added_Number
\n".$ADDED_METHODS;
}
$ADDED_METHODS = $Anchor.$ADDED_METHODS.$TOP_REF."
\n";
}
return $ADDED_METHODS;
}
sub get_Report_Removed($)
{
if($ShortMode) {
return "";
}
my $Level = $_[0];
my ($REMOVED_METHODS, %MethodRemovedFromArchiveClass);
foreach my $Method (sort keys(%CompatProblems))
{
foreach my $Kind (sort keys(%{$CompatProblems{$Method}}))
{
if($Kind eq "Removed_Method")
{
if($Level eq "Source")
{
if($ChangedReturnFromVoid{$Method}) {
next;
}
}
my $ArchiveName = $MethodInfo{1}{$Method}{"Archive"};
my $ClassName = get_ShortName($MethodInfo{1}{$Method}{"Class"}, 1);
$MethodRemovedFromArchiveClass{$ArchiveName}{$ClassName}{$Method} = 1;
}
}
}
my $Removed_Number = 0;
foreach my $ArchiveName (sort {lc($a) cmp lc($b)} keys(%MethodRemovedFromArchiveClass))
{
foreach my $ClassName (sort {lc($a) cmp lc($b)} keys(%{$MethodRemovedFromArchiveClass{$ArchiveName}}))
{
my %NameSpace_Method = ();
foreach my $Method (keys(%{$MethodRemovedFromArchiveClass{$ArchiveName}{$ClassName}}))
{
$NameSpace_Method{$MethodInfo{1}{$Method}{"Package"}}{$Method} = 1;
}
my $ShowClass = $ClassName;
$ShowClass=~s/<.*>//g;
foreach my $NameSpace (sort keys(%NameSpace_Method))
{
$REMOVED_METHODS .= "$ArchiveName, ".htmlSpecChars($ShowClass).".class
\n";
if($NameSpace) {
$REMOVED_METHODS .= "package $NameSpace
\n";
}
if($Compact) {
$REMOVED_METHODS .= "";
}
my @SortedMethods = sort {lc($MethodInfo{1}{$a}{"Signature"}) cmp lc($MethodInfo{1}{$b}{"Signature"})} keys(%{$NameSpace_Method{$NameSpace}});
foreach my $Method (@SortedMethods)
{
$Removed_Number += 1;
my $Signature = undef;
if($Compact) {
$Signature = get_Signature($Method, 1, "Full|HTML|Simple");
}
else {
$Signature = highLight_Signature_Italic_Color($Method, 1);
}
if($NameSpace) {
$Signature=~s/(\W|\A)\Q$NameSpace\E\.(\w)/$1$2/g;
}
if($Compact) {
$REMOVED_METHODS .= " ".$Signature."
\n";
}
else {
$REMOVED_METHODS .= insertIDs($ContentSpanStart.$Signature.$ContentSpanEnd."
\n".$ContentDivStart."[mangled: ".htmlSpecChars($Method)."]
".$ContentDivEnd."\n");
}
}
if($Compact) {
$REMOVED_METHODS .= "
";
}
$REMOVED_METHODS .= "
\n";
}
}
}
if($REMOVED_METHODS)
{
my $Anchor = "";
if($JoinReport) {
$Anchor = "";
}
if($OldStyle) {
$REMOVED_METHODS = "Removed Methods ($Removed_Number)
\n".$REMOVED_METHODS;
}
else {
$REMOVED_METHODS = "Removed Methods $Removed_Number
\n".$REMOVED_METHODS;
}
$REMOVED_METHODS = $Anchor.$REMOVED_METHODS.$TOP_REF."
\n";
}
return $REMOVED_METHODS;
}
sub get_Report_MethodProblems($$)
{
my ($TargetSeverity, $Level) = @_;
my $METHOD_PROBLEMS = "";
my (%ReportMap, %MethodChanges) = ();
foreach my $Method (sort keys(%CompatProblems))
{
my $ArchiveName = $MethodInfo{1}{$Method}{"Archive"};
my $ClassName = get_ShortName($MethodInfo{1}{$Method}{"Class"}, 1);
foreach my $Kind (sort keys(%{$CompatProblems{$Method}}))
{
if($Kind eq "Added_Method"
or $Kind eq "Removed_Method") {
next;
}
if(my $Severity = $MethodProblems_Kind{$Level}{$Kind})
{
if($Severity ne $TargetSeverity) {
next;
}
$MethodChanges{$Method}{$Kind} = $CompatProblems{$Method}{$Kind};
$ReportMap{$ArchiveName}{$ClassName}{$Method} = 1;
}
}
}
my $ProblemsNum = 0;
foreach my $ArchiveName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
{
foreach my $ClassName (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$ArchiveName}}))
{
my %NameSpace_Method = ();
foreach my $Method (keys(%{$ReportMap{$ArchiveName}{$ClassName}})) {
$NameSpace_Method{$MethodInfo{1}{$Method}{"Package"}}{$Method} = 1;
}
my $ShowClass = $ClassName;
$ShowClass=~s/<.*>//g;
foreach my $NameSpace (sort keys(%NameSpace_Method))
{
$METHOD_PROBLEMS .= "$ArchiveName, ".htmlSpecChars($ShowClass).".class
\n";
if($NameSpace) {
$METHOD_PROBLEMS .= "package $NameSpace
\n";
}
my @SortedMethods = sort {lc($MethodInfo{1}{$a}{"Signature"}) cmp lc($MethodInfo{1}{$b}{"Signature"})} keys(%{$NameSpace_Method{$NameSpace}});
foreach my $Method (@SortedMethods)
{
my $ShortSignature = get_Signature($Method, 1, "Short");
my $ClassName_Full = get_TypeName($MethodInfo{1}{$Method}{"Class"}, 1);
my $METHOD_REPORT = "";
my $ProblemNum = 1;
foreach my $Kind (sort keys(%{$MethodChanges{$Method}}))
{
foreach my $Location (sort keys(%{$MethodChanges{$Method}{$Kind}}))
{
my %Problems = %{$MethodChanges{$Method}{$Kind}{$Location}};
my $Target = $Problems{"Target"};
my ($Change, $Effect) = ("", "");
my $Old_Value = htmlSpecChars($Problems{"Old_Value"});
my $New_Value = htmlSpecChars($Problems{"New_Value"});
if($Kind eq "Method_Became_Static")
{
$Change = "Method became static.\n";
$Effect = "A client program may be interrupted by NoSuchMethodError exception.";
}
elsif($Kind eq "Method_Became_NonStatic")
{
$Change = "Method became non-static.\n";
if($Level eq "Binary") {
$Effect = "A client program may be interrupted by NoSuchMethodError exception.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: non-static method ".htmlSpecChars($ShortSignature)." cannot be referenced from a static context.";
}
}
elsif($Kind eq "Changed_Method_Return_From_Void")
{
$Change = "Return value type has been changed from void to ".htmlSpecChars($New_Value).".\n";
$Effect = "This method has been removed because the return type is part of the method signature.";
}
elsif($Kind eq "Static_Method_Became_Final")
{# Source Only
$Change = "Method became final.\n";
$Effect = "Recompilation of a client program may be terminated with the message: ".htmlSpecChars($ShortSignature)." in client class C cannot override ".htmlSpecChars($ShortSignature)." in ".htmlSpecChars($ClassName_Full)."; overridden method is final.";
}
elsif($Kind eq "NonStatic_Method_Became_Final")
{
$Change = "Method became final.\n";
if($Level eq "Binary") {
$Effect = "A client program trying to reimplement this method may be interrupted by VerifyError exception.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: ".htmlSpecChars($ShortSignature)." in client class C cannot override ".htmlSpecChars($ShortSignature)." in ".htmlSpecChars($ClassName_Full)."; overridden method is final.";
}
}
elsif($Kind eq "Method_Became_Abstract")
{
$Change = "Method became abstract.\n";
if($Level eq "Binary") {
$Effect = "A client program trying to create an instance of the method's class may be interrupted by InstantiationError exception.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: A client class C is not abstract and does not override abstract method ".htmlSpecChars($ShortSignature)." in ".htmlSpecChars($ClassName_Full).".";
}
}
elsif($Kind eq "Method_Became_NonAbstract")
{
$Change = "Method became non-abstract.\n";
if($Level eq "Binary") {
$Effect = "A client program may change behavior.";
}
else {
$Effect = "No effect.";
}
}
elsif($Kind eq "Method_Became_Default")
{
$Change = "Method became default.\n";
if($Level eq "Binary") {
$Effect = "No effect.";
}
else {
$Effect = "No effect.";
}
}
elsif($Kind eq "Method_Became_NonDefault")
{
$Change = "Method became non-default.\n";
if($Level eq "Binary") {
$Effect = "A client program trying to create an instance of a class may be interrupted by AbstractMethodError exception.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: A client class C is not abstract and does not override abstract method ".htmlSpecChars($ShortSignature)." in ".htmlSpecChars($ClassName_Full).".";
}
}
elsif($Kind eq "Method_Became_Synchronized")
{
$Change = "Method became synchronized.\n";
$Effect = "A multi-threaded client program may change behavior.";
}
elsif($Kind eq "Method_Became_NonSynchronized")
{
$Change = "Method became non-synchronized.\n";
$Effect = "A multi-threaded client program may change behavior.";
}
elsif($Kind eq "Changed_Method_Access")
{
$Change = "Access level has been changed from ".htmlSpecChars($Old_Value)." to ".htmlSpecChars($New_Value).".";
if($Level eq "Binary") {
$Effect = "A client program may be interrupted by IllegalAccessError exception.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: ".htmlSpecChars($ShortSignature)." has $New_Value access in ".htmlSpecChars($ClassName_Full).".";
}
}
elsif($Kind eq "Abstract_Method_Added_Checked_Exception")
{# Source Only
$Change = "Added ".htmlSpecChars($Target)." exception thrown.\n";
$Effect = "Recompilation of a client program may be terminated with the message: unreported exception ".htmlSpecChars($Target)." must be caught or declared to be thrown.";
}
elsif($Kind eq "NonAbstract_Method_Added_Checked_Exception")
{
$Change = "Added ".htmlSpecChars($Target)." exception thrown.\n";
if($Level eq "Binary") {
$Effect = "A client program may be interrupted by added exception.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: unreported exception ".htmlSpecChars($Target)." must be caught or declared to be thrown.";
}
}
elsif($Kind eq "Abstract_Method_Removed_Checked_Exception")
{# Source Only
$Change = "Removed ".htmlSpecChars($Target)." exception thrown.\n";
$Effect = "Recompilation of a client program may be terminated with the message: cannot override ".htmlSpecChars($ShortSignature)." in ".htmlSpecChars($ClassName_Full)."; overridden method does not throw ".htmlSpecChars($Target).".";
}
elsif($Kind eq "NonAbstract_Method_Removed_Checked_Exception")
{
$Change = "Removed ".htmlSpecChars($Target)." exception thrown.\n";
if($Level eq "Binary") {
$Effect = "A client program may change behavior because the removed exception will not be thrown any more and client will not catch and handle it.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: cannot override ".htmlSpecChars($ShortSignature)." in ".htmlSpecChars($ClassName_Full)."; overridden method does not throw ".htmlSpecChars($Target).".";
}
}
elsif($Kind eq "Added_Unchecked_Exception")
{# Binary Only
$Change = "Added ".htmlSpecChars($Target)." exception thrown.\n";
$Effect = "A client program may be interrupted by added exception.";
}
elsif($Kind eq "Removed_Unchecked_Exception")
{# Binary Only
$Change = "Removed ".htmlSpecChars($Target)." exception thrown.\n";
$Effect = "A client program may change behavior because the removed exception will not be thrown any more and client will not catch and handle it.";
}
if($Change)
{
$METHOD_REPORT .= "$ProblemNum | ".$Change." | ".$Effect." |
\n";
$ProblemNum += 1;
$ProblemsNum += 1;
}
}
}
$ProblemNum -= 1;
if($METHOD_REPORT)
{
my $ShowMethod = highLight_Signature_Italic_Color($Method, 1);
if($NameSpace)
{
$METHOD_REPORT = cut_Namespace($METHOD_REPORT, $NameSpace);
$ShowMethod = cut_Namespace($ShowMethod, $NameSpace);
}
$METHOD_PROBLEMS .= $ContentSpanStart."[+] ".$ShowMethod;
if($OldStyle) {
$METHOD_PROBLEMS .= " ($ProblemNum)";
}
else {
$METHOD_PROBLEMS .= " $ProblemNum ";
}
$METHOD_PROBLEMS .= $ContentSpanEnd."
\n";
$METHOD_PROBLEMS .= $ContentDivStart;
if(not $Compact) {
$METHOD_PROBLEMS .= " [mangled: ".htmlSpecChars($Method)."]
\n";
}
$METHOD_PROBLEMS .= " | Change | Effect |
$METHOD_REPORT
$ContentDivEnd\n";
}
}
$METHOD_PROBLEMS .= "
";
}
}
}
if($METHOD_PROBLEMS)
{
$METHOD_PROBLEMS = insertIDs($METHOD_PROBLEMS);
my $Title = "Problems with Methods, $TargetSeverity Severity";
if($TargetSeverity eq "Safe")
{ # Safe Changes
$Title = "Other Changes in Methods";
}
if($OldStyle) {
$METHOD_PROBLEMS = "$Title ($ProblemsNum)
\n".$METHOD_PROBLEMS;
}
else {
$METHOD_PROBLEMS = "$Title $ProblemsNum
\n".$METHOD_PROBLEMS;
}
$METHOD_PROBLEMS = "\n".$METHOD_PROBLEMS;
$METHOD_PROBLEMS .= $TOP_REF."
\n";
}
return $METHOD_PROBLEMS;
}
sub get_Report_TypeProblems($$)
{
my ($TargetSeverity, $Level) = @_;
my $TYPE_PROBLEMS = "";
my %ReportMap = ();
my %TypeChanges_Sev = ();
foreach my $TypeName (keys(%{$TypeChanges{$Level}}))
{
my $ArchiveName = $TypeInfo{1}{$TName_Tid{1}{$TypeName}}{"Archive"};
foreach my $Kind (keys(%{$TypeChanges{$Level}{$TypeName}}))
{
my $Severity = $TypeProblems_Kind{$Level}{$Kind};
if($Severity ne $TargetSeverity) {
next;
}
foreach my $Location (keys(%{$TypeChanges{$Level}{$TypeName}{$Kind}}))
{
$ReportMap{$ArchiveName}{$TypeName} = 1;
$TypeChanges_Sev{$TypeName}{$Kind}{$Location} = $TypeChanges{$Level}{$TypeName}{$Kind}{$Location};
}
}
}
my $ProblemsNum = 0;
foreach my $ArchiveName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
{
my ($HEADER_REPORT, %NameSpace_Type) = ();
foreach my $TypeName (keys(%{$ReportMap{$ArchiveName}}))
{
$NameSpace_Type{$TypeInfo{1}{$TName_Tid{1}{$TypeName}}{"Package"}}{$TypeName} = 1;
}
foreach my $NameSpace (sort keys(%NameSpace_Type))
{
$TYPE_PROBLEMS .= "$ArchiveName
\n";
if($NameSpace) {
$TYPE_PROBLEMS .= "package ".$NameSpace."
\n";
}
my @SortedTypes = sort {lc($a) cmp lc($b)} keys(%{$NameSpace_Type{$NameSpace}});
foreach my $TypeName (@SortedTypes)
{
my $TypeId = $TName_Tid{1}{$TypeName};
my $ProblemNum = 1;
my $TYPE_REPORT = "";
my (%Kinds_Locations, %Kinds_Target) = ();
foreach my $Kind (sort keys(%{$TypeChanges_Sev{$TypeName}}))
{
foreach my $Location (sort keys(%{$TypeChanges_Sev{$TypeName}{$Kind}}))
{
$Kinds_Locations{$Kind}{$Location} = 1;
my $Target = $TypeChanges_Sev{$TypeName}{$Kind}{$Location}{"Target"};
if($Kinds_Target{$Kind}{$Target}) {
next;
}
$Kinds_Target{$Kind}{$Target} = 1;
my ($Change, $Effect) = ("", "");
my %Problems = %{$TypeChanges_Sev{$TypeName}{$Kind}{$Location}};
my $Old_Value = $Problems{"Old_Value"};
my $New_Value = $Problems{"New_Value"};
my $Field_Type = $Problems{"Field_Type"};
my $Field_Value = $Problems{"Field_Value"};
my $Type_Type = $Problems{"Type_Type"};
if($Kind eq "NonAbstract_Class_Added_Abstract_Method")
{
$Change = "Abstract method ".black_Name($Target, 2)." has been added to this $Type_Type.";
if($Level eq "Binary") {
$Effect = "This class became abstract and a client program may be interrupted by InstantiationError exception.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method ".htmlSpecChars(get_Signature($Target, 2, "Short"))." in ".htmlSpecChars(get_TypeName($MethodInfo{2}{$Target}{"Class"}, 2)).".";
}
}
elsif($Kind eq "Abstract_Class_Added_Abstract_Method")
{
$Change = "Abstract method ".black_Name($Target, 2)." has been added to this $Type_Type.";
if($Level eq "Binary") {
$Effect = "No effect.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method ".htmlSpecChars(get_Signature($Target, 2, "Short"))." in ".htmlSpecChars(get_TypeName($MethodInfo{2}{$Target}{"Class"}, 2)).".";
}
}
elsif($Kind eq "Abstract_Class_Added_Abstract_Method_Invoked_By_Others")
{
$Change = "Abstract method ".black_Name($Target, 2)." has been added to this $Type_Type.";
if($Level eq "Binary") {
$Effect = "A client program may be interrupted by AbstractMethodError exception. Added abstract method is called in 2nd library version by the method ".black_Name($Problems{"InvokedBy"}, 1)." and may not be implemented by old clients.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method ".htmlSpecChars(get_Signature($Target, 2, "Short"))." in ".htmlSpecChars(get_TypeName($MethodInfo{2}{$Target}{"Class"}, 2)).".";
}
}
elsif($Kind eq "Class_Removed_Abstract_Method"
or $Kind eq "Interface_Removed_Abstract_Method")
{
$Change = "Abstract method ".black_Name($Target, 1)." has been removed from this $Type_Type.";
if($Level eq "Binary") {
$Effect = "A client program may be interrupted by NoSuchMethodError exception.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: cannot find method ".htmlSpecChars(get_Signature($Target, 1, "Short"))." in $Type_Type ".htmlSpecChars(get_TypeName($MethodInfo{1}{$Target}{"Class"}, 1)).".";
}
}
elsif($Kind eq "Interface_Added_Abstract_Method")
{
$Change = "Abstract method ".black_Name($Target, 2)." has been added to this $Type_Type.";
if($Level eq "Binary") {
$Effect = "No effect.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method ".htmlSpecChars(get_Signature($Target, 2, "Short"))." in ".htmlSpecChars(get_TypeName($MethodInfo{2}{$Target}{"Class"}, 2)).".";
}
}
elsif($Kind eq "Interface_Added_Abstract_Method_Invoked_By_Others")
{
$Change = "Abstract method ".black_Name($Target, 2)." has been added to this $Type_Type.";
if($Level eq "Binary") {
$Effect = "A client program may be interrupted by AbstractMethodError exception. Added abstract method is called in 2nd library version by the method ".black_Name($Problems{"InvokedBy"}, 1)." and may not be implemented by old clients.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method ".htmlSpecChars(get_Signature($Target, 2, "Short"))." in ".htmlSpecChars(get_TypeName($MethodInfo{2}{$Target}{"Class"}, 2)).".";
}
}
elsif($Kind eq "Class_Method_Became_Abstract")
{
$Change = "Method ".black_Name($Target, 1)." became abstract.";
if($Level eq "Binary") {
$Effect = "A client program may be interrupted by InstantiationError exception.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method ".htmlSpecChars(get_Signature($Target, 1, "Short"))." in ".htmlSpecChars(get_TypeName($MethodInfo{1}{$Target}{"Class"}, 1)).".";
}
}
elsif($Kind eq "Class_Method_Became_NonAbstract")
{
$Change = "Abstract method ".black_Name($Target, 1)." became non-abstract.";
if($Level eq "Binary") {
$Effect = "Some methods in this class may change behavior.";
}
else {
$Effect = "No effect.";
}
}
elsif($Kind eq "Interface_Method_Became_NonDefault")
{
$Change = "Method ".black_Name($Target, 1)." became non-default.";
if($Level eq "Binary") {
$Effect = "A client program may be interrupted by AbstractMethodError exception.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method ".htmlSpecChars(get_Signature($Target, 1, "Short"))." in ".htmlSpecChars(get_TypeName($MethodInfo{1}{$Target}{"Class"}, 1)).".";
}
}
elsif($Kind eq "Interface_Method_Became_Default")
{
$Change = "Method ".black_Name($Target, 1)." became default.";
if($Level eq "Binary") {
$Effect = "No effect.";
}
else {
$Effect = "No effect.";
}
}
elsif($Kind eq "Class_Overridden_Method")
{
$Change = "Method ".black_Name($Old_Value, 2)." has been overridden by ".black_Name($New_Value, 2);
$Effect = "Method ".black_Name($New_Value, 2)." will be called instead of ".black_Name($Old_Value, 2)." in a client program.";
}
elsif($Kind eq "Class_Method_Moved_Up_Hierarchy")
{
$Change = "Method ".black_Name($Old_Value, 1)." has been moved up type hierarchy to ".black_Name($New_Value, 2);
$Effect = "Method ".black_Name($New_Value, 2)." will be called instead of ".black_Name($Old_Value, 1)." in a client program.";
}
elsif($Kind eq "Abstract_Class_Added_Super_Interface")
{
$Change = "Added super-interface ".htmlSpecChars($Target).".";
if($Level eq "Binary")
{
$Effect = "No effect.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method in ".htmlSpecChars($Target).".";
}
}
elsif($Kind eq "Abstract_Class_Added_Super_Interface_Invoked_By_Others")
{
$Change = "Added super-interface ".htmlSpecChars($Target).".";
if($Level eq "Binary") {
$Effect = "If abstract methods from an added super-interface must be implemented by client then it may be interrupted by AbstractMethodError exception.
Abstract method ".black_Name_S(htmlSpecChars($Problems{"Invoked"}))." from the added super-interface is called by the method ".black_Name($Problems{"InvokedBy"}, 2)." in 2nd library version and may not be implemented by old clients.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method in ".htmlSpecChars($Target).".";
}
}
elsif($Kind eq "Abstract_Class_Added_Super_Interface_With_Implemented_Methods")
{
$Change = "Added super-interface ".htmlSpecChars($Target).".";
$Effect = "No effect.";
}
elsif($Kind eq "Interface_Added_Super_Interface")
{
$Change = "Added super-interface ".htmlSpecChars($Target).".";
if($Level eq "Binary") {
$Effect = "No effect.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method in ".htmlSpecChars($Target).".";
}
}
elsif($Kind eq "Interface_Added_Super_Interface_Used_By_Others")
{
$Change = "Added super-interface ".htmlSpecChars($Target).".";
if($Level eq "Binary")
{
$Effect = "If abstract methods from an added super-interface must be implemented by client then it may be interrupted by AbstractMethodError exception.
Abstract method ".black_Name_S(htmlSpecChars($Problems{"Invoked"}))." from the added super-interface is called by the method ".black_Name($Problems{"InvokedBy"}, 2)." in 2nd library version and may not be implemented by old clients.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method in ".htmlSpecChars($Target).".";
}
}
elsif($Kind eq "Interface_Added_Super_Interface_With_Implemented_Methods")
{
$Change = "Added super-interface ".htmlSpecChars($Target).".";
if($Level eq "Binary") {
$Effect = "No effect.";
}
else {
$Effect = "No effect.";
}
}
elsif($Kind eq "Interface_Added_Super_Constant_Interface")
{
$Change = "Added super-interface ".htmlSpecChars($Target)." containing constants only.";
if($Level eq "Binary") {
$Effect = "A static field from a super-interface of a client class may hide a field (with the same name) inherited from a super-class and cause IncompatibleClassChangeError exception.";
}
else {
$Effect = "A static field from a super-interface of a client class may hide a field (with the same name) inherited from a super-class. Recompilation of a client class may be terminated with the message: reference to variable is ambiguous.";
}
}
elsif($Kind eq "Interface_Removed_Super_Interface"
or $Kind eq "Class_Removed_Super_Interface")
{
$Change = "Removed super-interface ".htmlSpecChars($Target).".";
if($Level eq "Binary") {
$Effect = "A client program may be interrupted by NoSuchMethodError exception.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: cannot find method in $Type_Type ".htmlSpecChars($TypeName).".";
}
}
elsif($Kind eq "Interface_Removed_Super_Constant_Interface")
{
$Change = "Removed super-interface ".htmlSpecChars($Target)." containing constants only.";
if($Level eq "Binary") {
$Effect = "No effect.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: cannot find variable in $Type_Type ".htmlSpecChars($TypeName).".";
}
}
elsif($Kind eq "Added_Super_Class")
{
$Change = "Added super-class ".htmlSpecChars($Target).".";
if($Level eq "Binary") {
$Effect = "A static field from a super-interface of a client class may hide a field (with the same name) inherited from new super-class and cause IncompatibleClassChangeError exception.";
}
else {
$Effect = "A static field from a super-interface of a client class may hide a field (with the same name) inherited from new super-class. Recompilation of a client class may be terminated with the message: reference to variable is ambiguous.";
}
}
elsif($Kind eq "Abstract_Class_Added_Super_Abstract_Class")
{
$Change = "Added abstract super-class ".htmlSpecChars($Target).".";
if($Level eq "Binary") {
$Effect = "No effect.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method in ".htmlSpecChars($Target).".";
}
}
elsif($Kind eq "Abstract_Class_Added_Super_Abstract_Class_Invoked_By_Others")
{
$Change = "Added abstract super-class ".htmlSpecChars($Target).".";
if($Level eq "Binary") {
$Effect = "If abstract methods from an added super-class must be implemented by client then it may be interrupted by AbstractMethodError exception.
Abstract method ".black_Name_S(htmlSpecChars($Problems{"Invoked"}))." from the added abstract super-class is called by the method ".black_Name($Problems{"InvokedBy"}, 2)." in 2nd library version and may not be implemented by old clients.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method in ".htmlSpecChars($Target).".";
}
}
elsif($Kind eq "Removed_Super_Class")
{
$Change = "Removed super-class ".htmlSpecChars($Target).".";
if($Level eq "Binary") {
$Effect = "Access of a client program to the fields or methods of the old super-class may be interrupted by NoSuchFieldError or NoSuchMethodError exceptions.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: cannot find variable (or method) in ".htmlSpecChars($TypeName).".";
}
}
elsif($Kind eq "Changed_Super_Class")
{
$Change = "Superclass has been changed from ".htmlSpecChars($Old_Value)." to ".htmlSpecChars($New_Value).".";
if($Level eq "Binary") {
$Effect = "1) Access of a client program to the fields or methods of the old super-class may be interrupted by NoSuchFieldError or NoSuchMethodError exceptions.
2) A static field from a super-interface of a client class may hide a field (with the same name) inherited from new super-class and cause IncompatibleClassChangeError exception.";
}
else {
$Effect = "1) Recompilation of a client program may be terminated with the message: cannot find variable (or method) in ".htmlSpecChars($TypeName).".
2) A static field from a super-interface of a client class may hide a field (with the same name) inherited from new super-class. Recompilation of a client class may be terminated with the message: reference to variable is ambiguous.";
}
}
elsif($Kind eq "Class_Added_Field")
{
$Change = "Field $Target has been added to this class.";
if($Level eq "Binary")
{
$Effect = "No effect.";
# $Effect .= "
NOTE: A static field from a super-interface of a client class may hide an added field (with the same name) inherited from the super-class of a client class and cause IncompatibleClassChangeError exception.";
}
else
{
$Effect = "No effect.";
# $Effect .= "
NOTE: A static field from a super-interface of a client class may hide an added field (with the same name) inherited from the super-class of a client class. Recompilation of a client class may be terminated with the message: reference to $Target is ambiguous.";
}
}
elsif($Kind eq "Interface_Added_Field")
{
$Change = "Field $Target has been added to this interface.";
if($Level eq "Binary") {
$Effect = "No effect.
NOTE: An added static field from a super-interface of a client class may hide a field (with the same name) inherited from the super-class of a client class and cause IncompatibleClassChangeError exception.";
}
else {
$Effect = "No effect.
NOTE: An added static field from a super-interface of a client class may hide a field (with the same name) inherited from the super-class of a client class. Recompilation of a client class may be terminated with the message: reference to $Target is ambiguous.";
}
}
elsif($Kind eq "Renamed_Field")
{
$Change = "Field $Target has been renamed to ".htmlSpecChars($New_Value).".";
if($Level eq "Binary") {
$Effect = "A client program may be interrupted by NoSuchFieldError exception.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: cannot find variable $Target in ".htmlSpecChars($TypeName).".";
}
}
elsif($Kind eq "Renamed_Constant_Field")
{
if($Level eq "Binary") {
$Change = "Field $Target (".htmlSpecChars($Field_Type).") with the compile-time constant value $Field_Value has been renamed to ".htmlSpecChars($New_Value).".";
$Effect = "A client program may change behavior.";
}
else {
$Change = "Field $Target has been renamed to ".htmlSpecChars($New_Value).".";
$Effect = "Recompilation of a client program may be terminated with the message: cannot find variable $Target in ".htmlSpecChars($TypeName).".";
}
}
elsif($Kind eq "Removed_NonConstant_Field")
{
$Change = "Field $Target of type ".htmlSpecChars($Field_Type)." has been removed from this $Type_Type.";
if($Level eq "Binary") {
$Effect = "A client program may be interrupted by NoSuchFieldError exception.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: cannot find variable $Target in ".htmlSpecChars($TypeName).".";
}
}
elsif($Kind eq "Removed_Constant_Field")
{
$Change = "Field $Target (".htmlSpecChars($Field_Type).") with the compile-time constant value $Field_Value has been removed from this $Type_Type.";
if($Level eq "Binary") {
$Effect = "A client program may change behavior.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: cannot find variable $Target in ".htmlSpecChars($TypeName).".";
}
}
elsif($Kind eq "Changed_Field_Type")
{
$Change = "Type of field $Target has been changed from ".htmlSpecChars($Old_Value)." to ".htmlSpecChars($New_Value).".";
if($Level eq "Binary") {
$Effect = "A client program may be interrupted by NoSuchFieldError exception.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: incompatible types, found: ".htmlSpecChars($Old_Value).", required: ".htmlSpecChars($New_Value).".";
}
}
elsif($Kind eq "Changed_Field_Access")
{
$Change = "Access level of field $Target has been changed from $Old_Value to $New_Value.";
if($Level eq "Binary") {
$Effect = "A client program may be interrupted by IllegalAccessError exception.";
}
else
{
if($New_Value eq "package-private") {
$Effect = "Recompilation of a client program may be terminated with the message: $Target is not public in ".htmlSpecChars($TypeName)."; cannot be accessed from outside package.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: $Target has $New_Value access in ".htmlSpecChars($TypeName).".";
}
}
}
elsif($Kind eq "Changed_Final_Field_Value")
{ # Binary Only
$Change = "Value of final field $Target ($Field_Type) has been changed from ".htmlSpecChars($Old_Value)." to ".htmlSpecChars($New_Value).".";
$Effect = "Old value of the field will be inlined to the client code at compile-time and will be used instead of a new one.";
}
elsif($Kind eq "Changed_Final_Version_Field_Value")
{ # Binary Only
$Change = "Value of final field $Target ($Field_Type) has been changed from ".htmlSpecChars($Old_Value)." to ".htmlSpecChars($New_Value).".";
$Effect = "Old value of the field will be inlined to the client code at compile-time and will be used instead of a new one.";
}
elsif($Kind eq "Field_Became_Final")
{
$Change = "Field $Target became final.";
if($Level eq "Binary") {
$Effect = "A client program may be interrupted by IllegalAccessError exception when attempt to assign new values to the field.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: cannot assign a value to final variable $Target.";
}
}
elsif($Kind eq "Field_Became_NonFinal")
{ # Binary Only
$Change = "Field $Target became non-final.";
$Effect = "Old value of the field will be inlined to the client code at compile-time and will be used instead of a new one.";
}
elsif($Kind eq "NonConstant_Field_Became_Static")
{ # Binary Only
$Change = "Non-final field $Target became static.";
$Effect = "A client program may be interrupted by IncompatibleClassChangeError exception.";
}
elsif($Kind eq "NonConstant_Field_Became_NonStatic")
{
if($Level eq "Binary") {
$Change = "Non-constant field $Target became non-static.";
$Effect = "A client program may be interrupted by IncompatibleClassChangeError exception.";
}
else {
$Change = "Field $Target became non-static.";
$Effect = "Recompilation of a client program may be terminated with the message: non-static variable $Target cannot be referenced from a static context.";
}
}
elsif($Kind eq "Constant_Field_Became_NonStatic")
{ # Source Only
$Change = "Field $Target became non-static.";
$Effect = "Recompilation of a client program may be terminated with the message: non-static variable $Target cannot be referenced from a static context.";
}
elsif($Kind eq "Class_Became_Interface")
{
$Change = "This class became interface.";
if($Level eq "Binary") {
$Effect = "A client program may be interrupted by IncompatibleClassChangeError or InstantiationError exception dependent on the usage of this class.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: ".htmlSpecChars($TypeName)." is abstract; cannot be instantiated.";
}
}
elsif($Kind eq "Interface_Became_Class")
{
$Change = "This interface became class.";
if($Level eq "Binary") {
$Effect = "A client program may be interrupted by IncompatibleClassChangeError exception.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: interface expected.";
}
}
elsif($Kind eq "Class_Became_Final")
{
$Change = "This class became final.";
if($Level eq "Binary") {
$Effect = "A client program may be interrupted by VerifyError exception.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: cannot inherit from final ".htmlSpecChars($TypeName).".";
}
}
elsif($Kind eq "Class_Became_Abstract")
{
$Change = "This class became abstract.";
if($Level eq "Binary") {
$Effect = "A client program may be interrupted by InstantiationError exception.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: ".htmlSpecChars($TypeName)." is abstract; cannot be instantiated.";
}
}
elsif($Kind eq "Removed_Class")
{
$Change = "This class has been removed.";
if($Level eq "Binary") {
$Effect = "A client program may be interrupted by NoClassDefFoundError exception.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: cannot find class ".htmlSpecChars($TypeName).".";
}
}
elsif($Kind eq "Removed_Interface")
{
$Change = "This interface has been removed.";
if($Level eq "Binary") {
$Effect = "A client program may be interrupted by NoClassDefFoundError exception.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the message: cannot find class ".htmlSpecChars($TypeName).".";
}
}
elsif($Kind eq "Removed_Annotation")
{
$Change = "This annotation type has been removed.";
if($Level eq "Binary") {
$Effect = "No effect.";
}
else {
$Effect = "Recompilation of a client program may be terminated with the error message: cannot find symbol \@".htmlSpecChars($TypeName).".";
}
}
if($Change)
{
$TYPE_REPORT .= "$ProblemNum | ".$Change." | ".$Effect." |
\n";
$ProblemNum += 1;
$ProblemsNum += 1;
$Kinds_Locations{$Kind}{$Location} = 1;
}
}
}
$ProblemNum -= 1;
if($TYPE_REPORT)
{
my $Affected = "";
if(not defined $TypeInfo{1}{$TypeId}{"Annotation"}) {
$Affected = getAffectedMethods($Level, $TypeName, \%Kinds_Locations);
}
my $ShowType = $TypeName;
if($NameSpace)
{
$TYPE_REPORT = cut_Namespace($TYPE_REPORT, $NameSpace);
$ShowType = cut_Namespace($ShowType, $NameSpace);
$Affected = cut_Namespace($Affected, $NameSpace);
}
$TYPE_PROBLEMS .= $ContentSpanStart."[+] ".htmlSpecChars($ShowType);
if($OldStyle) {
$TYPE_PROBLEMS .= " ($ProblemNum)";
}
else {
$TYPE_PROBLEMS .= " $ProblemNum ";
}
$TYPE_PROBLEMS .= $ContentSpanEnd."
\n";
$TYPE_PROBLEMS .= $ContentDivStart."";
$TYPE_PROBLEMS .= " | Change | Effect | ";
$TYPE_PROBLEMS .= "
$TYPE_REPORT
".$Affected."
$ContentDivEnd\n";
}
}
$TYPE_PROBLEMS .= "
";
}
}
if($TYPE_PROBLEMS)
{
$TYPE_PROBLEMS = insertIDs($TYPE_PROBLEMS);
my $Title = "Problems with Data Types, $TargetSeverity Severity";
if($TargetSeverity eq "Safe")
{ # Safe Changes
$Title = "Other Changes in Data Types";
}
if($OldStyle) {
$TYPE_PROBLEMS = "$Title ($ProblemsNum)
\n".$TYPE_PROBLEMS;
}
else {
$TYPE_PROBLEMS = "$Title $ProblemsNum
\n".$TYPE_PROBLEMS;
}
$TYPE_PROBLEMS = "\n".$TYPE_PROBLEMS;
$TYPE_PROBLEMS .= $TOP_REF."
\n";
}
return $TYPE_PROBLEMS;
}
sub cut_Namespace($$)
{
my ($N, $Ns) = @_;
$N=~s/(\W|\A)\Q$Ns\E\.(\w)/$1$2/g;
return $N;
}
sub getAffectedMethods($$$)
{
my ($Level, $Target_TypeName, $Kinds_Locations) = @_;
my $LIMIT = 10;
if(defined $AffectLimit) {
$LIMIT = $AffectLimit;
}
my @Kinds = sort keys(%{$Kinds_Locations});
my %KLocs = ();
foreach my $Kind (@Kinds)
{
my @Locs = sort {$a=~/retval/ cmp $b=~/retval/} sort {length($a)<=>length($b)} sort keys(%{$Kinds_Locations->{$Kind}});
$KLocs{$Kind} = \@Locs;
}
my %SymLocKind = ();
foreach my $Method (sort keys(%{$TypeProblemsIndex{$Target_TypeName}}))
{
if($Method eq ".client_method") {
next;
}
foreach my $Kind (@Kinds)
{
foreach my $Loc (@{$KLocs{$Kind}})
{
if(not defined $CompatProblems{$Method}{$Kind}{$Loc}) {
next;
}
my $Type_Name = $CompatProblems{$Method}{$Kind}{$Loc}{"Type_Name"};
if($Type_Name ne $Target_TypeName) {
next;
}
$SymLocKind{$Method}{$Loc}{$Kind} = 1;
last;
}
}
}
%KLocs = (); # clear
if(not keys(%SymLocKind)) {
return "";
}
my %SymSel = ();
my $Num = 0;
foreach my $Method (sort keys(%SymLocKind))
{
LOOP: foreach my $Loc (sort {$a=~/retval/ cmp $b=~/retval/} sort {length($a)<=>length($b)} sort keys(%{$SymLocKind{$Method}}))
{
foreach my $Kind (sort keys(%{$SymLocKind{$Method}{$Loc}}))
{
$SymSel{$Method}{"Loc"} = $Loc;
$SymSel{$Method}{"Kind"} = $Kind;
last LOOP;
}
}
$Num += 1;
if($Num>=$LIMIT) {
last;
}
}
my $Affected = "";
foreach my $Method (sort {lc($a) cmp lc($b)} keys(%SymSel))
{
my $Kind = $SymSel{$Method}{"Kind"};
my $Loc = $SymSel{$Method}{"Loc"};
my $Desc = getAffectDesc($Method, $Kind, $Loc, $Level);
my $PName = getParamName($Loc);
my $Pos = getParamPos($PName, $Method, 1);
$Affected .= "".get_Signature($Method, 1, "HTML|Italic|Param|Class|Target=".$Pos)."
";
$Affected .= "".$Desc."
\n";
}
if(keys(%SymLocKind)>$LIMIT) {
$Affected .= " ...\n
\n"; # and others ...
}
$Affected = "".$Affected."
";
if($Affected)
{
my $Num = keys(%SymLocKind);
my $Per = show_number($Num*100/keys(%CheckedMethods));
$Affected = $ContentDivStart.$Affected.$ContentDivEnd;
$Affected = $ContentSpanStart_Affected."[+] affected methods: $Num ($Per\%)".$ContentSpanEnd.$Affected;
}
return ($Affected);
}
sub getAffectDesc($$$$)
{
my ($Method, $Kind, $Location, $Level) = @_;
my %Affect = %{$CompatProblems{$Method}{$Kind}{$Location}};
my $New_Value = $Affect{"New_Value"};
my $Type_Name = $Affect{"Type_Name"};
my @Sentence_Parts = ();
$Location=~s/\.[^.]+?\Z//;
my %TypeAttr = get_Type($MethodInfo{1}{$Method}{"Class"}, 1);
my $Type_Type = $TypeAttr{"Type"};
my $ABSTRACT_M = $MethodInfo{1}{$Method}{"Abstract"}?" abstract":"";
my $ABSTRACT_C = $TypeAttr{"Abstract"}?" abstract":"";
my $METHOD_TYPE = $MethodInfo{1}{$Method}{"Constructor"}?"constructor":"method";
if($Kind eq "Class_Overridden_Method" or $Kind eq "Class_Method_Moved_Up_Hierarchy") {
return "Method '".highLight_Signature($New_Value, 2)."' will be called instead of this method in a client program.";
}
elsif($TypeProblems_Kind{$Level}{$Kind})
{
my %MInfo = %{$MethodInfo{1}{$Method}};
if($Location eq "this") {
return "This$ABSTRACT_M $METHOD_TYPE is from \'".htmlSpecChars($Type_Name)."\'$ABSTRACT_C $Type_Type.";
}
my $TypeID = undef;
if($Location=~/retval/)
{ # return value
if($Location=~/\./) {
push(@Sentence_Parts, "Field \'".htmlSpecChars($Location)."\' in return value");
}
else {
push(@Sentence_Parts, "Return value");
}
$TypeID = $MInfo{"Return"};
}
elsif($Location=~/this/)
{ # "this" reference
push(@Sentence_Parts, "Field \'".htmlSpecChars($Location)."\' in the object");
$TypeID = $MInfo{"Class"};
}
else
{ # parameters
my $PName = getParamName($Location);
my $PPos = getParamPos($PName, $Method, 1);
if($Location=~/\./) {
push(@Sentence_Parts, "Field \'".htmlSpecChars($Location)."\' in ".showPos($PPos)." parameter");
}
else {
push(@Sentence_Parts, showPos($PPos)." parameter");
}
if($PName) {
push(@Sentence_Parts, "\'$PName\'");
}
if(defined $MInfo{"Param"}) {
$TypeID = $MInfo{"Param"}{$PPos}{"Type"};
}
}
push(@Sentence_Parts, " of this$ABSTRACT_M method");
my $Location_T = $Location;
$Location_T=~s/\A\w+(\.|\Z)//; # location in type
my $TypeID_Problem = $TypeID;
if($Location_T) {
$TypeID_Problem = getFieldType($Location_T, $TypeID, 1);
}
if($TypeInfo{1}{$TypeID_Problem}{"Name"} eq $Type_Name) {
push(@Sentence_Parts, "has type \'".htmlSpecChars($Type_Name)."\'.");
}
else {
push(@Sentence_Parts, "has base type \'".htmlSpecChars($Type_Name)."\'.");
}
}
return join(" ", @Sentence_Parts);
}
sub getParamPos($$$)
{
my ($Name, $Method, $LibVersion) = @_;
if(defined $MethodInfo{$LibVersion}{$Method}
and defined $MethodInfo{$LibVersion}{$Method}{"Param"})
{
my $Info = $MethodInfo{$LibVersion}{$Method};
foreach (keys(%{$Info->{"Param"}}))
{
if($Info->{"Param"}{$_}{"Name"} eq $Name)
{
return $_;
}
}
}
return undef;
}
sub getParamName($)
{
my $Loc = $_[0];
$Loc=~s/\..*//g;
return $Loc;
}
sub getFieldType($$$)
{
my ($Location, $TypeId, $LibVersion) = @_;
my @Fields = split(/\./, $Location);
foreach my $Name (@Fields)
{
my %Info = get_BaseType($TypeId, $LibVersion);
foreach my $N (keys(%{$Info{"Fields"}}))
{
if($N eq $Name)
{
$TypeId = $Info{"Fields"}{$N}{"Type"};
last;
}
}
}
return $TypeId;
}
sub writeReport($$)
{
my ($Level, $Report) = @_;
my $RPath = getReportPath($Level);
writeFile($RPath, $Report);
}
sub getRelPath($$)
{
my ($A, $B) = @_;
return abs2rel($A, get_dirname($B));
}
sub createReport()
{
if($JoinReport)
{ # --stdout
writeReport("Join", getReport("Join"));
}
elsif($DoubleReport)
{ # default
writeReport("Binary", getReport("Binary"));
writeReport("Source", getReport("Source"));
}
elsif($BinaryOnly)
{ # --binary
writeReport("Binary", getReport("Binary"));
}
elsif($SourceOnly)
{ # --source
writeReport("Source", getReport("Source"));
}
}
sub getCssStyles()
{
my $CssStyles = "
body {
font-family:Arial, sans-serif;
background-color:White;
color:Black;
}
hr {
color:Black;
background-color:Black;
height:1px;
border:0;
}
h1 {
margin-bottom:0px;
padding-bottom:0px;
font-size:1.625em;
}
h2 {
margin-bottom:0px;
padding-bottom:0px;
font-size:1.25em;
white-space:nowrap;
}
div.symbols {
color:#003E69;
}
div.symbols i {
color:Brown;
}
span.section {
font-weight:bold;
cursor:pointer;
color:#003E69;
white-space:nowrap;
margin-left:5px;
}
span:hover.section {
color:#336699;
}
span.sect_aff {
cursor:pointer;
margin-left:7px;
padding-left:15px;
font-size:0.875em;
color:#cc3300;
}
span.ext {
font-weight:100;
}
span.jar {
color:#cc3300;
font-size:0.875em;
font-weight:bold;
}
div.jar_list {
padding-left:5px;
font-size:0.94em;
}
span.pkg_t {
color:#408080;
font-size:0.875em;
}
span.pkg {
color:#408080;
font-size:0.875em;
font-weight:bold;
}
span.cname {
color:Green;
font-size:0.875em;
font-weight:bold;
}
span.iname_b {
font-weight:bold;
font-size:1.1em;
}
span.iname_a {
color:#333333;
font-weight:bold;
font-size:0.94em;
}
span.sym_p {
font-weight:normal;
white-space:normal;
}
span.sym_p span {
white-space:nowrap;
}
span.attr {
color:Black;
font-weight:100;
}
span.deprecated {
color:Red;
font-weight:bold;
font-family:Monaco, monospace;
}
div.affect {
padding-left:15px;
padding-bottom:10px;
font-size:0.87em;
font-style:italic;
line-height:0.75em;
}
div.affected {
padding-left:30px;
padding-top:10px;
}
table.ptable {
border-collapse:collapse;
border:1px outset black;
line-height:1em;
margin-left:15px;
margin-top:3px;
margin-bottom:3px;
width:900px;
}
table.ptable td {
border:1px solid Gray;
padding: 3px;
font-size:0.875em;
text-align:left;
vertical-align:top;
}
table.ptable th {
background-color:#eeeeee;
font-weight:bold;
color:#333333;
font-family:Verdana, Arial;
font-size:0.875em;
border:1px solid Gray;
text-align:center;
vertical-align:top;
white-space:nowrap;
padding: 3px;
}
table.summary {
border-collapse:collapse;
border:1px outset black;
}
table.summary th {
background-color:#eeeeee;
font-weight:100;
text-align:left;
font-size:0.94em;
white-space:nowrap;
border:1px inset Gray;
padding: 3px;
}
table.summary td {
text-align:right;
white-space:nowrap;
border:1px inset Gray;
padding: 3px 5px 3px 10px;
}
span.mngl {
padding-left:15px;
font-size:0.875em;
cursor:text;
color:#444444;
}
span.color_p {
font-style:italic;
color:Brown;
}
span.param {
font-style:italic;
}
span.focus_p {
font-style:italic;
background-color:#DCDCDC;
}
span.nowrap {
white-space:nowrap;
}
.passed {
background-color:#CCFFCC;
font-weight:100;
}
.warning {
background-color:#F4F4AF;
font-weight:100;
}
.failed {
background-color:#FFCCCC;
font-weight:100;
}
.new {
background-color:#C6DEFF;
font-weight:100;
}
.compatible {
background-color:#CCFFCC;
font-weight:100;
}
.almost_compatible {
background-color:#FFDAA3;
font-weight:100;
}
.incompatible {
background-color:#FFCCCC;
font-weight:100;
}
.gray {
background-color:#DCDCDC;
font-weight:100;
}
.top_ref {
font-size:0.69em;
}
.footer {
font-size:0.75em;
}";
if($JoinReport or $ExternCss)
{
$CssStyles .= "
.tabset {
float:left;
}
a.tab {
border:1px solid Black;
float:left;
margin:0px 5px -1px 0px;
padding:3px 5px 3px 5px;
position:relative;
font-size:0.875em;
background-color:#DDD;
text-decoration:none;
color:Black;
}
a.disabled:hover
{
color:Black;
background:#EEE;
}
a.active:hover
{
color:Black;
background:White;
}
a.active {
border-bottom-color:White;
background-color:White;
}
div.tab {
border-top:1px solid Black;
padding:0px;
width:100%;
clear:both;
}";
}
return $CssStyles;
}
sub getJsScript()
{
my $JScripts = "
function sC(header, id)
{
e = document.getElementById(id);
if(e.style.display == 'none')
{
e.style.display = 'block';
e.style.visibility = 'visible';
header.innerHTML = header.innerHTML.replace(/\\\[[^0-9 ]\\\]/gi,\"[−]\");
}
else
{
e.style.display = 'none';
e.style.visibility = 'hidden';
header.innerHTML = header.innerHTML.replace(/\\\[[^0-9 ]\\\]/gi,\"[+]\");
}
}";
if($JoinReport or $ExternJs)
{
$JScripts .= "
function initTabs()
{
var url = window.location.href;
if(url.indexOf('_Source_')!=-1 || url.indexOf('#Source')!=-1)
{
var tab1 = document.getElementById('BinaryID');
var tab2 = document.getElementById('SourceID');
tab1.className='tab disabled';
tab2.className='tab active';
}
var sets = document.getElementsByTagName('div');
for (var i = 0; i < sets.length; i++)
{
if (sets[i].className.indexOf('tabset') != -1)
{
var tabs = [];
var links = sets[i].getElementsByTagName('a');
for (var j = 0; j < links.length; j++)
{
if (links[j].className.indexOf('tab') != -1)
{
tabs.push(links[j]);
links[j].tabs = tabs;
var tab = document.getElementById(links[j].href.substr(links[j].href.indexOf('#') + 1));
//reset all tabs on start
if (tab)
{
if (links[j].className.indexOf('active')!=-1) {
tab.style.display = 'block';
}
else {
tab.style.display = 'none';
}
}
links[j].onclick = function()
{
var tab = document.getElementById(this.href.substr(this.href.indexOf('#') + 1));
if (tab)
{
//reset all tabs before change
for (var k = 0; k < this.tabs.length; k++)
{
document.getElementById(this.tabs[k].href.substr(this.tabs[k].href.indexOf('#') + 1)).style.display = 'none';
this.tabs[k].className = this.tabs[k].className.replace('active', 'disabled');
}
this.className = 'tab active';
tab.style.display = 'block';
// window.location.hash = this.id.replace('ID', '');
return false;
}
}
}
}
}
}
if(url.indexOf('#')!=-1) {
location.href=location.href;
}
}
if (window.addEventListener) window.addEventListener('load', initTabs, false);
else if (window.attachEvent) window.attachEvent('onload', initTabs);";
}
return $JScripts;
}
sub getReport($)
{
my $Level = $_[0];
my $CssStyles = getCssStyles();
my $JScripts = getJsScript();
if(defined $ExternCss)
{
$CssStyles=~s/\n /\n/g;
writeFile($ExternCss, $CssStyles);
}
if(defined $ExternJs)
{
$JScripts=~s/\n /\n/g;
writeFile($ExternJs, $JScripts);
}
if($Level eq "Join")
{
my $Title = "$TargetTitle: ".$Descriptor{1}{"Version"}." to ".$Descriptor{2}{"Version"}." compatibility report";
my $Keywords = "$TargetTitle, compatibility";
my $Description = "Compatibility report for the $TargetTitle library between ".$Descriptor{1}{"Version"}." and ".$Descriptor{2}{"Version"}." versions";
my ($BSummary, $BMetaData) = get_Summary("Binary");
my ($SSummary, $SMetaData) = get_Summary("Source");
my $Report = "\n\n".composeHTML_Head($Level, $Title, $Keywords, $Description, $CssStyles, $JScripts)."";
$Report .= get_Report_Header("Join")."
";
$Report .= "\n$BSummary\n".get_Report_Added("Binary").get_Report_Removed("Binary").get_Report_Problems("High", "Binary").get_Report_Problems("Medium", "Binary").get_Report_Problems("Low", "Binary").get_Report_Problems("Safe", "Binary").get_SourceInfo()."
";
$Report .= "\n$SSummary\n".get_Report_Added("Source").get_Report_Removed("Source").get_Report_Problems("High", "Source").get_Report_Problems("Medium", "Source").get_Report_Problems("Low", "Source").get_Report_Problems("Safe", "Source").get_SourceInfo()."
";
$Report .= getReportFooter();
$Report .= "\n