source: josm/trunk/tools/japicc/japi-compliance-checker.pl

Last change on this file was 13595, checked in by Don-vip, 5 years ago

tools update: Groovy 2.4.15, PMD 6.2.0, JAPICC 2.4

File size: 169.7 KB
Line 
1#!/usr/bin/perl
2#########################################################################
3# Java API Compliance Checker (JAPICC) 2.4
4# A tool for checking backward compatibility of a Java library API
5#
6# Written by Andrey Ponomarenko
7#
8# Copyright (C) 2011-2018 Andrey Ponomarenko's ABI Laboratory
9#
10# PLATFORMS
11# =========
12#  Linux, FreeBSD, Mac OS X, MS Windows
13#
14# REQUIREMENTS
15# ============
16#  Linux, FreeBSD, Mac OS X
17#    - JDK or OpenJDK - development files (javap, javac, jar, jmod)
18#    - Perl 5 (5.8 or newer)
19#
20#  MS Windows
21#    - JDK or OpenJDK (javap, javac, jar, jmod)
22#    - Active Perl 5 (5.8 or newer)
23#
24# This library is free software; you can redistribute it and/or
25# modify it under the terms of the GNU Lesser General Public
26# License as published by the Free Software Foundation; either
27# version 2.1 of the License, or (at your option) any later version.
28#
29# This library is distributed in the hope that it will be useful,
30# but WITHOUT ANY WARRANTY; without even the implied warranty of
31# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
32# Lesser General Public License for more details.
33#
34# You should have received a copy of the GNU Lesser General Public
35# License along with this library; if not, write to the Free Software
36# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
37# MA  02110-1301  USA.
38#########################################################################
39use Getopt::Long;
40Getopt::Long::Configure ("posix_default", "no_ignore_case", "permute");
41use File::Path qw(mkpath rmtree);
42use File::Temp qw(tempdir);
43use File::Basename qw(dirname);
44use Cwd qw(abs_path cwd);
45use Data::Dumper;
46
47my $TOOL_VERSION = "2.4";
48my $API_DUMP_VERSION = "2.4";
49my $API_DUMP_VERSION_MIN = "2.2";
50
51# Internal modules
52my $MODULES_DIR = getModules();
53push(@INC, dirname($MODULES_DIR));
54
55# Basic modules
56my %LoadedModules = ();
57loadModule("Basic");
58loadModule("Input");
59loadModule("Path");
60loadModule("Logging");
61loadModule("Utils");
62loadModule("TypeAttr");
63loadModule("Filter");
64loadModule("SysFiles");
65loadModule("Descriptor");
66loadModule("Mangling");
67
68# Rules DB
69my %RULES_PATH = (
70"Binary" => $MODULES_DIR."/RulesBin.xml",
71"Source" => $MODULES_DIR."/RulesSrc.xml");
72
73my $CmdName = getFilename($0);
74
75my %HomePage = (
76    "Dev"=>"https://github.com/lvc/japi-compliance-checker",
77    "Doc"=>"https://lvc.github.io/japi-compliance-checker/"
78);
79
80my $ShortUsage = "Java API Compliance Checker (JAPICC) $TOOL_VERSION
81A tool for checking backward compatibility of a Java library API
82Copyright (C) 2018 Andrey Ponomarenko's ABI Laboratory
83License: LGPLv2.1+
84
85Usage: $CmdName [options]
86Example:
87 $CmdName OLD.jar NEW.jar
88 $CmdName OLD.jmod NEW.jmod
89
90More info: $CmdName --help";
91
92if($#ARGV==-1)
93{
94    printMsg("INFO", $ShortUsage);
95    exit(0);
96}
97
98GetOptions("h|help!" => \$In::Opt{"Help"},
99  "v|version!" => \$In::Opt{"ShowVersion"},
100  "dumpversion!" => \$In::Opt{"DumpVersion"},
101# general options
102  "l|lib|library=s" => \$In::Opt{"TargetLib"},
103  "d1|old|o=s" => \$In::Desc{1}{"Path"},
104  "d2|new|n=s" => \$In::Desc{2}{"Path"},
105# extra options
106  "client|app=s" => \$In::Opt{"ClientPath"},
107  "binary|bin!" => \$In::Opt{"BinaryOnly"},
108  "source|src!" => \$In::Opt{"SourceOnly"},
109  "v1|version1|vnum=s" => \$In::Desc{1}{"TargetVersion"},
110  "v2|version2=s" => \$In::Desc{2}{"TargetVersion"},
111  "s|strict!" => \$In::Opt{"StrictCompat"},
112  "keep-internal!" => \$In::Opt{"KeepInternal"},
113  "skip-internal-packages|skip-internal=s" => \$In::Opt{"SkipInternalPackages"},
114  "skip-internal-types=s" => \$In::Opt{"SkipInternalTypes"},
115  "dump|dump-api=s" => \$In::Opt{"DumpAPI"},
116  "check-annotations!" => \$In::Opt{"CheckAnnotations"},
117  "check-packages=s" => \$In::Opt{"CheckPackages"},
118  "classes-list=s" => \$In::Opt{"ClassListPath"},
119  "annotations-list=s" => \$In::Opt{"AnnotationsListPath"},
120  "skip-annotations-list=s" => \$In::Opt{"SkipAnnotationsListPath"},
121  "skip-deprecated!" => \$In::Opt{"SkipDeprecated"},
122  "skip-classes=s" => \$In::Opt{"SkipClassesList"},
123  "skip-packages=s" => \$In::Opt{"SkipPackagesList"},
124  "non-impl=s" => \$In::Opt{"NonImplClassesList"},
125  "non-impl-all!" => \$In::Opt{"NonImplAll"},
126  "short" => \$In::Opt{"ShortMode"},
127  "dump-path=s" => \$In::Opt{"OutputDumpPath"},
128  "report-path=s" => \$In::Opt{"OutputReportPath"},
129  "bin-report-path=s" => \$In::Opt{"BinaryReportPath"},
130  "src-report-path=s" => \$In::Opt{"SourceReportPath"},
131  "quick!" => \$In::Opt{"Quick"},
132  "sort!" => \$In::Opt{"SortDump"},
133  "show-access!" => \$In::Opt{"ShowAccess"},
134  "limit-affected=s" => \$In::Opt{"AffectLimit"},
135  "hide-templates!" => \$In::Opt{"HideTemplates"},
136  "show-packages!" => \$In::Opt{"ShowPackages"},
137  "compact!" => \$In::Opt{"Compact"},
138  "added-annotations!" => \$In::Opt{"AddedAnnotations"},
139  "removed-annotations!" => \$In::Opt{"RemovedAnnotations"},
140  "count-methods=s" => \$In::Opt{"CountMethods"},
141  "dep1=s" => \$In::Desc{1}{"DepDump"},
142  "dep2=s" => \$In::Desc{2}{"DepDump"},
143  "old-style!" => \$In::Opt{"OldStyle"},
144# other options
145  "test!" => \$In::Opt{"TestTool"},
146  "debug!" => \$In::Opt{"Debug"},
147  "title=s" => \$In::Opt{"TargetTitle"},
148  "jdk-path=s" => \$In::Opt{"JdkPath"},
149  "external-css=s" => \$In::Opt{"ExternCss"},
150  "external-js=s" => \$In::Opt{"ExternJs"},
151# deprecated
152  "minimal!" => \$In::Opt{"Minimal"},
153  "hide-packages!" => \$In::Opt{"HidePackages"},
154# private
155  "all-affected!" => \$In::Opt{"AllAffected"}
156) or errMsg();
157
158if(@ARGV)
159{ 
160    if($#ARGV==1)
161    { # japi-compliance-checker OLD.jar NEW.jar
162        $In::Desc{1}{"Path"} = $ARGV[0];
163        $In::Desc{2}{"Path"} = $ARGV[1];
164    }
165    else {
166        errMsg();
167    }
168}
169
170sub errMsg()
171{
172    printMsg("INFO", "\n".$ShortUsage);
173    exit(getErrorCode("Error"));
174}
175
176my $HelpMessage = "
177NAME:
178  Java API Compliance Checker ($CmdName)
179  Check backward compatibility of a Java library API
180
181DESCRIPTION:
182  Java API Compliance Checker (JAPICC) is a tool for checking backward
183  binary/source compatibility of a Java library API. The tool checks class
184  declarations of old and new versions and analyzes changes that may break
185  compatibility: removed class members, added abstract methods, etc.
186 
187  Break of the binary compatibility may result in crash or incorrect behavior
188  of existing clients built with an old library version if they run with a
189  new one. Break of the source compatibility may result in recompilation
190  errors with a new library version.
191
192  The tool is intended for developers of software libraries and maintainers
193  of operating systems who are interested in ensuring backward compatibility,
194  i.e. allow old clients to run or to be recompiled with newer library
195  versions.
196
197  This tool is free software: you can redistribute it and/or modify it
198  under the terms of the GNU LGPL.
199
200USAGE:
201  $CmdName [options]
202
203EXAMPLE (1):
204  $CmdName OLD.jar NEW.jar
205
206EXAMPLE (2):
207  $CmdName OLD.jmod NEW.jmod
208
209EXAMPLE (3):
210  $CmdName -lib NAME -old OLD.xml -new NEW.xml
211  OLD.xml and NEW.xml are XML-descriptors:
212
213    <version>
214        1.0
215    </version>
216   
217    <archives>
218        /path1/to/JAR(s) OR JMOD(s)/
219        /path2/to/JAR(s) OR JMOD(s)/
220        ...
221    </archives>
222
223INFORMATION OPTIONS:
224  -h|-help
225      Print this help.
226
227  -v|-version
228      Print version information.
229
230  -dumpversion
231      Print the tool version ($TOOL_VERSION) and don't do anything else.
232
233GENERAL OPTIONS:
234  -l|-library NAME
235      Library name (without version).
236
237  -old|-d1 PATH
238      Descriptor of the 1st (old) library version.
239      It may be one of the following:
240     
241         1. Java archive (*.jar or *.jmod)
242         2. XML-descriptor (VERSION.xml file):
243
244              <version>
245                  1.0
246              </version>
247             
248              <archives>
249                  /path1/to/JAR(s) OR JMOD(s)/
250                  /path2/to/JAR(s) OR JMOD(s)/
251                   ...
252              </archives>
253
254                 ...
255         
256         3. API dump generated by -dump option
257
258      If you are using *.jar or *.jmod as a descriptor then
259      you should specify version numbers with -v1 and -v2
260      options too. If version numbers are not specified then
261      the tool will try to detect them automatically.
262
263  -new|-d2 PATH
264      Descriptor of the 2nd (new) library version.
265
266EXTRA OPTIONS:
267  -client|-app PATH
268      This option allows to specify the client Java archive that should be
269      checked for portability to the new library version.
270
271  -binary|-bin
272      Show \"Binary\" compatibility problems only.
273      Generate report to \"bin_compat_report.html\".
274
275  -source|-src
276      Show \"Source\" compatibility problems only.
277      Generate report to \"src_compat_report.html\".
278
279  -v1|-version1 NUM
280      Specify 1st API version outside the descriptor. This option is needed
281      if you have prefered an alternative descriptor type (see -d1 option).
282     
283      In general case you should specify it in the XML descriptor:
284          <version>
285              VERSION
286          </version>
287
288  -v2|-version2 NUM
289      Specify 2nd library version outside the descriptor.
290
291  -vnum NUM
292      Specify the library version in the generated API dump.
293
294  -s|-strict
295      Treat all API compatibility warnings as problems.
296
297  -keep-internal
298      Do not skip checking of these packages:
299        *impl*
300        *internal*
301        *examples*
302 
303  -skip-internal-packages PATTERN
304      Do not check packages matched by the regular expression.
305 
306  -skip-internal-types PATTERN
307      Do not check types (classes and interfaces) matched by the regular
308      expression. It's matched against full qualified type names (e.g.
309      'org.xyz.Name<T>'). It has to match any part of type name.
310 
311  -dump|-dump-api PATH
312      Dump library API to gzipped TXT format file. You can transfer it
313      anywhere and pass instead of the descriptor. Also it may be used
314      for debugging the tool. PATH is the path to the Java archive or
315      XML descriptor of the library.
316 
317  -check-annotations
318      Check for changes in annotations like in any other interfaces.
319 
320  -check-packages PATTERN
321      Check packages matched by the regular expression. Other packages
322      will not be checked.
323 
324  -classes-list PATH
325      This option allows to specify a file with a list
326      of classes that should be checked, other classes will not be checked.
327 
328  -annotations-list PATH
329      Specifies a file with a list of annotations. The tool will check only
330      classes annotated by the annotations from the list. Other classes
331      will not be checked.
332 
333  -skip-annotations-list PATH
334      Skip checking of classes annotated by the annotations in the list.
335     
336  -skip-deprecated
337      Skip analysis of deprecated methods and classes.
338     
339  -skip-classes PATH
340      List of classes that should not be checked.
341 
342  -skip-packages PATH
343      List of packages that should not be checked.
344 
345  -non-impl PATH
346      List of interfaces that should not be implemented by users.
347 
348  -non-impl-all
349      All interfaces should not be implemented by users.
350 
351  -short
352      Do not list added/removed methods.
353 
354  -dump-path PATH
355      Specify a *.dump file path where to generate an API dump.
356      Default:
357          api_dumps/LIB_NAME/VERSION/API.dump
358
359  -report-path PATH
360      Path to compatibility report.
361      Default:
362          compat_reports/LIB_NAME/V1_to_V2/compat_report.html
363
364  -bin-report-path PATH
365      Path to \"Binary\" compatibility report.
366      Default:
367          compat_reports/LIB_NAME/V1_to_V2/bin_compat_report.html
368
369  -src-report-path PATH
370      Path to \"Source\" compatibility report.
371      Default:
372          compat_reports/LIB_NAME/V1_to_V2/src_compat_report.html
373
374  -quick
375      Quick analysis.
376      Disabled:
377        - analysis of method parameter names
378        - analysis of class field values
379        - analysis of usage of added abstract methods
380        - distinction of deprecated methods and classes
381
382  -sort
383      Enable sorting of data in API dumps.
384     
385  -show-access
386      Show access level of non-public methods listed in the report.
387     
388  -hide-templates
389      Hide template parameters in the report.
390 
391  -hide-packages
392  -minimal
393      Do nothing.
394 
395  -show-packages
396      Show package names in the report.
397     
398  -limit-affected LIMIT
399      The maximum number of affected methods listed under the description
400      of the changed type in the report.
401 
402  -compact
403      Try to simplify formatting and reduce size of the report (for a big
404      set of changes).
405 
406  -added-annotations
407      Apply filters by annotations only to new version of the library.
408 
409  -removed-annotations
410      Apply filters by annotations only to old version of the library.
411 
412  -count-methods PATH
413      Count total public methods in the API dump.
414 
415  -dep1 PATH
416  -dep2 PATH
417      Path to the API dump of the required dependency archive. It will
418      be used to resolve overwritten methods and more.
419 
420  -old-style
421      Generate old-style report.
422
423OTHER OPTIONS:
424  -test
425      Run internal tests. Create two incompatible versions of a sample library
426      and run the tool to check them for compatibility. This option allows to
427      check if the tool works correctly in the current environment.
428
429  -debug
430      Debugging mode. Print debug info on the screen. Save intermediate
431      analysis stages in the debug directory:
432          debug/LIB_NAME/VER/
433
434      Also consider using -dump option for debugging the tool.
435
436  -title NAME
437      Change library name in the report title to NAME. By default
438      will be displayed a name specified by -l option.
439
440  -jdk-path PATH
441      Path to the JDK install tree (e.g. /usr/lib/jvm/java-7-openjdk-amd64).
442
443  -external-css PATH
444      Generate CSS styles file to PATH. This helps to save space when
445      generating thousands of reports.
446
447  -external-js PATH
448      Generate JS script file to PATH.
449
450REPORT:
451    Compatibility report will be generated to:
452        compat_reports/LIB_NAME/V1_to_V2/compat_report.html
453
454EXIT CODES:
455    0 - Compatible. The tool has run without any errors.
456    non-zero - Incompatible or the tool has run with errors.
457
458MORE INFO:
459    ".$HomePage{"Doc"}."
460    ".$HomePage{"Dev"};
461
462sub helpMsg() {
463    printMsg("INFO", $HelpMessage."\n");
464}
465
466#Aliases
467my (%MethodInfo, %TypeInfo, %TName_Tid) = ();
468
469my %TName_Tid_Generic = ();
470
471#Separate checked and unchecked exceptions
472my %KnownRuntimeExceptions= map {$_=>1} (
473    "java.lang.AnnotationTypeMismatchException",
474    "java.lang.ArithmeticException",
475    "java.lang.ArrayStoreException",
476    "java.lang.BufferOverflowException",
477    "java.lang.BufferUnderflowException",
478    "java.lang.CannotRedoException",
479    "java.lang.CannotUndoException",
480    "java.lang.ClassCastException",
481    "java.lang.CMMException",
482    "java.lang.ConcurrentModificationException",
483    "java.lang.DataBindingException",
484    "java.lang.DOMException",
485    "java.lang.EmptyStackException",
486    "java.lang.EnumConstantNotPresentException",
487    "java.lang.EventException",
488    "java.lang.IllegalArgumentException",
489    "java.lang.IllegalMonitorStateException",
490    "java.lang.IllegalPathStateException",
491    "java.lang.IllegalStateException",
492    "java.lang.ImagingOpException",
493    "java.lang.IncompleteAnnotationException",
494    "java.lang.IndexOutOfBoundsException",
495    "java.lang.JMRuntimeException",
496    "java.lang.LSException",
497    "java.lang.MalformedParameterizedTypeException",
498    "java.lang.MirroredTypeException",
499    "java.lang.MirroredTypesException",
500    "java.lang.MissingResourceException",
501    "java.lang.NegativeArraySizeException",
502    "java.lang.NoSuchElementException",
503    "java.lang.NoSuchMechanismException",
504    "java.lang.NullPointerException",
505    "java.lang.ProfileDataException",
506    "java.lang.ProviderException",
507    "java.lang.RasterFormatException",
508    "java.lang.RejectedExecutionException",
509    "java.lang.SecurityException",
510    "java.lang.SystemException",
511    "java.lang.TypeConstraintException",
512    "java.lang.TypeNotPresentException",
513    "java.lang.UndeclaredThrowableException",
514    "java.lang.UnknownAnnotationValueException",
515    "java.lang.UnknownElementException",
516    "java.lang.UnknownEntityException",
517    "java.lang.UnknownTypeException",
518    "java.lang.UnmodifiableSetException",
519    "java.lang.UnsupportedOperationException",
520    "java.lang.WebServiceException",
521    "java.lang.WrongMethodTypeException"
522);
523
524#java.lang.Object
525my %JavaObjectMethod = (
526   
527    "java/lang/Object.clone:()Ljava/lang/Object;" => 1,
528    "java/lang/Object.equals:(Ljava/lang/Object;)Z" => 1,
529    "java/lang/Object.finalize:()V" => 1,
530    "java/lang/Object.getClass:()Ljava/lang/Class;" => 1,
531    "java/lang/Object.hashCode:()I" => 1,
532    "java/lang/Object.notify:()V" => 1,
533    "java/lang/Object.notifyAll:()V" => 1,
534    "java/lang/Object.toString:()Ljava/lang/String;" => 1,
535    "java/lang/Object.wait:()V" => 1,
536    "java/lang/Object.wait:(J)V" => 1,
537    "java/lang/Object.wait:(JI)V" => 1
538);
539
540#Global variables
541my %Cache;
542my %RESULT;
543my $TOP_REF = "<a class='top_ref' href='#Top'>to the top</a>";
544
545#Types
546my %CheckedTypes;
547
548#Classes
549my %LibArchives;
550my %Class_Methods;
551my %Class_Methods_Generic;
552my %Class_AbstractMethods;
553my %Class_Fields;
554my %ClassMethod_AddedUsed;
555my %Class_Constructed;
556
557#Methods
558my %CheckedMethods;
559my %MethodUsed;
560my %OldMethodSignature;
561
562#Merging
563my %AddedMethod_Abstract;
564my %RemovedMethod_Abstract;
565my %ChangedReturnFromVoid;
566my %CompatRules;
567my %IncompleteRules;
568my %UnknownRules;
569
570#Report
571my %TypeChanges;
572
573#Recursion locks
574my @RecurTypes;
575
576#Problem descriptions
577my %CompatProblems;
578my %TotalAffected;
579
580#Speedup
581my %TypeProblemsIndex;
582
583#Rerort
584my $ContentID = 1;
585my $ContentSpanStart = "<span class=\"section\" onclick=\"sC(this, 'CONTENT_ID')\">\n";
586my $ContentSpanStart_Affected = "<span class=\"sect_aff\" onclick=\"sC(this, 'CONTENT_ID')\">\n";
587my $ContentSpanEnd = "</span>\n";
588my $ContentDivStart = "<div id=\"CONTENT_ID\" style=\"display:none;\">\n";
589my $ContentDivEnd = "</div>\n";
590my $Content_Counter = 0;
591
592sub getModules()
593{
594    my $TOOL_DIR = dirname($0);
595    if(not $TOOL_DIR)
596    { # patch for MS Windows
597        $TOOL_DIR = ".";
598    }
599    my @SEARCH_DIRS = (
600        # tool's directory
601        abs_path($TOOL_DIR),
602        # relative path to modules
603        abs_path($TOOL_DIR)."/../share/japi-compliance-checker",
604        # install path
605        'MODULES_INSTALL_PATH'
606    );
607    foreach my $DIR (@SEARCH_DIRS)
608    {
609        if($DIR!~/\A(\/|\w+:[\/\\])/)
610        { # relative path
611            $DIR = abs_path($TOOL_DIR)."/".$DIR;
612        }
613        if(-d $DIR."/modules") {
614            return $DIR."/modules";
615        }
616    }
617   
618    print STDERR "ERROR: can't find modules (Did you installed the tool by 'make install' command?)\n";
619    exit(9); # Module_Error
620}
621
622sub loadModule($)
623{
624    my $Name = $_[0];
625    if(defined $LoadedModules{$Name}) {
626        return;
627    }
628    my $Path = $MODULES_DIR."/Internals/$Name.pm";
629    if(not -f $Path)
630    {
631        print STDERR "can't access \'$Path\'\n";
632        exit(2);
633    }
634    require $Path;
635    $LoadedModules{$Name} = 1;
636}
637
638sub readModule($$)
639{
640    my ($Module, $Name) = @_;
641    my $Path = $MODULES_DIR."/Internals/$Module/".$Name;
642    if(not -f $Path) {
643        exitStatus("Module_Error", "can't access \'$Path\'");
644    }
645    return readFile($Path);
646}
647
648sub mergeClasses()
649{
650    my %ReportedRemoved = undef;
651   
652    foreach my $ClassName (keys(%{$Class_Methods{1}}))
653    {
654        next if(not $ClassName);
655        my $Type1_Id = $TName_Tid{1}{$ClassName};
656        my $Type1 = getType($Type1_Id, 1);
657       
658        if($Type1->{"Type"}!~/class|interface/) {
659            next;
660        }
661       
662        if(defined $Type1->{"Access"}
663        and $Type1->{"Access"}=~/private/) {
664            next;
665        }
666       
667        if(not classFilter($Type1, 1, 1)) {
668            next;
669        }
670       
671        my $GenericName = getGeneric($ClassName);
672        my $Type2_Id = undef;
673       
674        if(defined $TName_Tid{2}{$ClassName}) {
675            $Type2_Id = $TName_Tid{2}{$ClassName};
676        }
677        elsif(defined $TName_Tid_Generic{2}{$GenericName}) {
678            $Type2_Id = $TName_Tid_Generic{2}{$GenericName};
679        }
680       
681        if($Type2_Id)
682        {
683            my $TName1 = $Type1->{"Name"};
684            my $TName2 = getTypeName($Type2_Id, 2);
685           
686            my $Generic1 = (index($TName1, "<")!=-1);
687            my $Generic2 = (index($TName2, "<")!=-1);
688           
689            if($Generic1 ne $Generic2)
690            { # removed generic parameters
691                foreach my $Method (keys(%{$Class_Methods{1}{$ClassName}}))
692                {
693                    if(not methodFilter($Method, 1)) {
694                        next;
695                    }
696                   
697                    $CheckedTypes{$ClassName} = 1;
698                    $CheckedMethods{$Method} = 1;
699                   
700                    if($Type1->{"Type"} eq "class")
701                    {
702                        if($Generic1)
703                        {
704                            %{$CompatProblems{$Method}{"Class_Became_Raw"}{"this"}} = (
705                                "Type_Name"=>$ClassName,
706                                "New_Value"=>$TName2,
707                                "Target"=>$ClassName);
708                        }
709                        else
710                        {
711                            %{$CompatProblems{$Method}{"Class_Became_Generic"}{"this"}} = (
712                                "Type_Name"=>$ClassName,
713                                "New_Value"=>$TName2,
714                                "Target"=>$ClassName);
715                        }
716                    }
717                    else
718                    {
719                        if($Generic1)
720                        {
721                            %{$CompatProblems{$Method}{"Interface_Became_Raw"}{"this"}} = (
722                                "Type_Name"=>$ClassName,
723                                "New_Value"=>$TName2,
724                                "Target"=>$ClassName);
725                        }
726                        else
727                        {
728                            %{$CompatProblems{$Method}{"Interface_Became_Generic"}{"this"}} = (
729                                "Type_Name"=>$ClassName,
730                                "New_Value"=>$TName2,
731                                "Target"=>$ClassName);
732                        }
733                    }
734                }
735            }
736        }
737        else
738        { # classes and interfaces with public methods
739            foreach my $Method (keys(%{$Class_Methods{1}{$ClassName}}))
740            {
741                if(not methodFilter($Method, 1)) {
742                    next;
743                }
744               
745                $CheckedTypes{$ClassName} = 1;
746                $CheckedMethods{$Method} = 1;
747               
748                if($Type1->{"Type"} eq "class")
749                {
750                    %{$CompatProblems{$Method}{"Removed_Class"}{"this"}} = (
751                        "Type_Name"=>$ClassName,
752                        "Target"=>$ClassName);
753                }
754                else
755                {
756                    %{$CompatProblems{$Method}{"Removed_Interface"}{"this"}} = (
757                        "Type_Name"=>$ClassName,
758                        "Target"=>$ClassName);
759                }
760               
761                $ReportedRemoved{$ClassName} = 1;
762            }
763        }
764    }
765   
766    foreach my $Class_Id (keys(%{$TypeInfo{1}}))
767    {
768        my $Class1 = getType($Class_Id, 1);
769       
770        if($Class1->{"Type"}!~/class|interface/) {
771            next;
772        }
773       
774        if(defined $Class1->{"Access"}
775        and $Class1->{"Access"}=~/private/) {
776            next;
777        }
778       
779        if(not classFilter($Class1, 1, 1)) {
780            next;
781        }
782       
783        my $ClassName = $Class1->{"Name"};
784        my $GenericName = getGeneric($ClassName);
785        my $Class2_Id = undef;
786       
787        if(defined $TName_Tid{2}{$ClassName}) {
788            $Class2_Id = $TName_Tid{2}{$ClassName};
789        }
790        elsif(defined $TName_Tid_Generic{2}{$GenericName}) {
791            $Class2_Id = $TName_Tid_Generic{2}{$GenericName};
792        }
793       
794        if($Class2_Id)
795        { # classes and interfaces with public static fields
796            if(not defined $Class_Methods{1}{$ClassName})
797            {
798                my $Class2 = getType($Class2_Id, 2);
799               
800                foreach my $Field (keys(%{$Class1->{"Fields"}}))
801                {
802                    my $FieldInfo = $Class1->{"Fields"}{$Field};
803                   
804                    my $FAccess = $FieldInfo->{"Access"};
805                    if($FAccess=~/private/) {
806                        next;
807                    }
808                   
809                    if($FieldInfo->{"Static"})
810                    {
811                        $CheckedTypes{$ClassName} = 1;
812                       
813                        if(not defined $Class2->{"Fields"}{$Field})
814                        {
815                            %{$CompatProblems{".client_method"}{"Removed_NonConstant_Field"}{$Field}}=(
816                                "Target"=>$Field,
817                                "Type_Name"=>$ClassName,
818                                "Type_Type"=>$Class1->{"Type"},
819                                "Field_Type"=>getTypeName($FieldInfo->{"Type"}, 1));
820                        }
821                    }
822                }
823            }
824           
825            if(defined $Class1->{"Annotation"})
826            {
827                my %AnnParam = ();
828                foreach my $VN (1, 2)
829                {
830                    foreach my $Method (keys(%{$Class_Methods{$VN}{$ClassName}}))
831                    {
832                        my $MInfo = $MethodInfo{$VN}{$Method};
833                        $AnnParam{$VN}{$MInfo->{"ShortName"}} = {"Default"=>$MInfo->{"Default"}, "Return"=>getTypeName($MInfo->{"Return"}, $VN)};
834                    }
835                }
836                foreach my $AParam (sort keys(%{$AnnParam{1}}))
837                {
838                    my $R1 = $AnnParam{1}{$AParam}{"Return"};
839                    my $D1 = $AnnParam{1}{$AParam}{"Default"};
840                   
841                    if(defined $AnnParam{2}{$AParam})
842                    {
843                        my $R2 = $AnnParam{2}{$AParam}{"Return"};
844                        my $D2 = $AnnParam{2}{$AParam}{"Default"};
845                       
846                        if($R1 ne $R2)
847                        {
848                            if($R1 eq "java.lang.String" and $R2 eq "java.lang.String[]")
849                            {
850                                %{$CompatProblems{".client_method"}{"Annotation_Element_Changed_Type_Safe"}{$AParam}} = (
851                                    "Type_Name"=>$ClassName,
852                                    "Old_Value"=>$R1,
853                                    "New_Value"=>$R2,
854                                    "Target"=>$AParam);
855                            }
856                            else
857                            {
858                                %{$CompatProblems{".client_method"}{"Annotation_Element_Changed_Type"}{$AParam}} = (
859                                    "Type_Name"=>$ClassName,
860                                    "Old_Value"=>$R1,
861                                    "New_Value"=>$R2,
862                                    "Target"=>$AParam);
863                            }
864                        }
865                       
866                        if(defined $D1 and not defined $D2)
867                        {
868                            %{$CompatProblems{".client_method"}{"Annotation_Element_Removed_Default_Value"}{$AParam}} = (
869                                "Type_Name"=>$ClassName,
870                                "Old_Value"=>$D1,
871                                "Target"=>$AParam);
872                        }
873                        elsif(not defined $D1 and defined $D2)
874                        {
875                            %{$CompatProblems{".client_method"}{"Annotation_Element_Added_Default_Value"}{$AParam}} = (
876                                "Type_Name"=>$ClassName,
877                                "New_Value"=>$D2,
878                                "Target"=>$AParam);
879                        }
880                        elsif($D1 ne $D2)
881                        {
882                            %{$CompatProblems{".client_method"}{"Annotation_Element_Changed_Default_Value"}{$AParam}} = (
883                                "Type_Name"=>$ClassName,
884                                "Old_Value"=>$D1,
885                                "New_Value"=>$D2,
886                                "Target"=>$AParam);
887                        }
888                    }
889                    else
890                    {
891                        if(defined $D1)
892                        {
893                            %{$CompatProblems{".client_method"}{"Removed_Annotation_Default_Element"}{$AParam}} = (
894                                "Type_Name"=>$ClassName,
895                                "Elem_Type"=>$R1,
896                                "Old_Value"=>$D1,
897                                "Target"=>$AParam);
898                        }
899                        else
900                        {
901                            %{$CompatProblems{".client_method"}{"Removed_Annotation_NonDefault_Element"}{$AParam}} = (
902                                "Type_Name"=>$ClassName,
903                                "Elem_Type"=>$R1,
904                                "Target"=>$AParam);
905                        }
906                    }
907                }
908               
909                foreach my $AParam (sort keys(%{$AnnParam{2}}))
910                {
911                    if(not defined $AnnParam{1}{$AParam})
912                    {
913                        my $R2 = $AnnParam{2}{$AParam}{"Return"};
914                       
915                        if(defined $AnnParam{2}{$AParam}{"Default"})
916                        {
917                            my $D2 = $AnnParam{2}{$AParam}{"Default"};
918                            %{$CompatProblems{".client_method"}{"Added_Annotation_Default_Element"}{$AParam}} = (
919                                "Type_Name"=>$ClassName,
920                                "Elem_Type"=>$R2,
921                                "New_Value"=>$D2,
922                                "Target"=>$AParam);
923                        }
924                        else
925                        {
926                            %{$CompatProblems{".client_method"}{"Added_Annotation_NonDefault_Element"}{$AParam}} = (
927                                "Type_Name"=>$ClassName,
928                                "Elem_Type"=>$R2,
929                                "Target"=>$AParam);
930                        }
931                    }
932                }
933            }
934        }
935        else
936        { # removed
937            if(defined $Class1->{"Annotation"})
938            {
939                %{$CompatProblems{".client_method"}{"Removed_Annotation"}{"this"}} = (
940                    "Type_Name"=>$ClassName,
941                    "Target"=>$ClassName);
942            }
943           
944            if(not defined $Class_Methods{1}{$ClassName})
945            {
946                # classes and interfaces with public static fields
947                if(not defined $ReportedRemoved{$ClassName})
948                {
949                    foreach my $Field (keys(%{$Class1->{"Fields"}}))
950                    {
951                        my $FieldInfo = $Class1->{"Fields"}{$Field};
952                       
953                        my $FAccess = $FieldInfo->{"Access"};
954                        if($FAccess=~/private/) {
955                            next;
956                        }
957                       
958                        if($FieldInfo->{"Static"})
959                        {
960                            $CheckedTypes{$ClassName} = 1;
961                           
962                            if($Class1->{"Type"} eq "class")
963                            {
964                                %{$CompatProblems{".client_method"}{"Removed_Class"}{"this"}} = (
965                                    "Type_Name"=>$ClassName,
966                                    "Target"=>$ClassName);
967                            }
968                            else
969                            {
970                                %{$CompatProblems{".client_method"}{"Removed_Interface"}{"this"}} = (
971                                    "Type_Name"=>$ClassName,
972                                    "Target"=>$ClassName);
973                            }
974                        }
975                    }
976                }
977            }
978        }
979    }
980}
981
982sub findFieldPair($$)
983{
984    my ($Field_Pos, $Pair_Type) = @_;
985    foreach my $Pair_Name (sort keys(%{$Pair_Type->{"Fields"}}))
986    {
987        if(defined $Pair_Type->{"Fields"}{$Pair_Name})
988        {
989            if($Pair_Type->{"Fields"}{$Pair_Name}{"Pos"} eq $Field_Pos) {
990                return $Pair_Name;
991            }
992        }
993    }
994    return "lost";
995}
996
997my %Severity_Val=(
998    "High"=>3,
999    "Medium"=>2,
1000    "Low"=>1,
1001    "Safe"=>-1
1002);
1003
1004sub isRecurType($$)
1005{
1006    foreach (@RecurTypes)
1007    {
1008        if($_->{"Tid1"} eq $_[0]
1009        and $_->{"Tid2"} eq $_[1])
1010        {
1011            return 1;
1012        }
1013    }
1014    return 0;
1015}
1016
1017sub pushType($$)
1018{
1019    my %TypeDescriptor = (
1020        "Tid1"  => $_[0],
1021        "Tid2"  => $_[1]);
1022    push(@RecurTypes, \%TypeDescriptor);
1023}
1024
1025sub getSFormat($)
1026{
1027    my $Name = $_[0];
1028    $Name=~s/\./\//g;
1029    return $Name;
1030}
1031
1032sub getConstantValue($$)
1033{
1034    my ($Value, $ValueType) = @_;
1035   
1036    if(not defined $Value) {
1037        return undef;
1038    }
1039   
1040    if($Value eq "\@EMPTY_STRING\@") {
1041        return "\"\"";
1042    }
1043    elsif($ValueType eq "java.lang.String") {
1044        return "\"".$Value."\"";
1045    }
1046   
1047    return $Value;
1048}
1049
1050sub getInvoked($)
1051{
1052    my $TName = $_[0];
1053   
1054    if(my @Invoked = sort keys(%{$ClassMethod_AddedUsed{$TName}}))
1055    {
1056        my $MFirst = $Invoked[0];
1057        my $MSignature = unmangle($MFirst);
1058        $MSignature=~s/\A.+\.(\w+\()/$1/g; # short name
1059        my $InvokedBy = $ClassMethod_AddedUsed{$TName}{$MFirst};
1060        return ($MSignature, $InvokedBy);
1061    }
1062   
1063    return ();
1064}
1065
1066sub mergeTypes($$)
1067{
1068    my ($Type1_Id, $Type2_Id) = @_;
1069    return {} if(not $Type1_Id or not $Type2_Id);
1070   
1071    if(defined $Cache{"mergeTypes"}{$Type1_Id}{$Type2_Id})
1072    { # already merged
1073        return $Cache{"mergeTypes"}{$Type1_Id}{$Type2_Id};
1074    }
1075   
1076    if(isRecurType($Type1_Id, $Type2_Id))
1077    { # do not follow to recursive declarations
1078        return {};
1079    }
1080   
1081    my %Type1 = %{getType($Type1_Id, 1)};
1082    my %Type2 = %{getType($Type2_Id, 2)};
1083   
1084    return {} if(not $Type1{"Name"} or not $Type2{"Name"});
1085    return {} if(not $Type1{"Archive"} or not $Type2{"Archive"});
1086    if($Type1{"Name"} ne $Type2{"Name"})
1087    {
1088        if(getGeneric($Type1{"Name"}) ne getGeneric($Type2{"Name"}))
1089        { # compare type declarations if became generic or raw
1090            return {};
1091        }
1092    }
1093   
1094    if(not classFilter(\%Type1, 1, 1)) {
1095        return {};
1096    }
1097   
1098    $CheckedTypes{$Type1{"Name"}} = 1;
1099   
1100    my %SubProblems = ();
1101   
1102    if($Type1{"BaseType"} and $Type2{"BaseType"})
1103    { # check base type (arrays)
1104        return mergeTypes($Type1{"BaseType"}, $Type2{"BaseType"});
1105    }
1106   
1107    if($Type2{"Type"}!~/(class|interface)/) {
1108        return {};
1109    }
1110   
1111    if($Type1{"Type"} eq "class" and not $Class_Constructed{1}{$Type1_Id})
1112    { # class cannot be constructed or inherited by clients
1113        return {};
1114    }
1115   
1116    if($Type1{"Type"} eq "class"
1117    and $Type2{"Type"} eq "interface")
1118    {
1119        %{$SubProblems{"Class_Became_Interface"}{""}}=(
1120            "Type_Name"=>$Type1{"Name"});
1121       
1122        return ($Cache{"mergeTypes"}{$Type1_Id}{$Type2_Id} = \%SubProblems);
1123    }
1124    if($Type1{"Type"} eq "interface"
1125    and $Type2{"Type"} eq "class")
1126    {
1127        %{$SubProblems{"Interface_Became_Class"}{""}}=(
1128            "Type_Name"=>$Type1{"Name"});
1129       
1130        return ($Cache{"mergeTypes"}{$Type1_Id}{$Type2_Id} = \%SubProblems);
1131    }
1132    if(not $Type1{"Final"}
1133    and $Type2{"Final"})
1134    {
1135        %{$SubProblems{"Class_Became_Final"}{""}}=(
1136            "Type_Name"=>$Type1{"Name"},
1137            "Target"=>$Type1{"Name"});
1138    }
1139    if(not $Type1{"Abstract"}
1140    and $Type2{"Abstract"})
1141    {
1142        %{$SubProblems{"Class_Became_Abstract"}{""}}=(
1143            "Type_Name"=>$Type1{"Name"});
1144    }
1145   
1146    pushType($Type1_Id, $Type2_Id);
1147   
1148    foreach my $AddedMethod (keys(%{$AddedMethod_Abstract{$Type2{"Name"}}}))
1149    {
1150        if($Type1{"Type"} eq "class")
1151        {
1152            if($Type1{"Abstract"})
1153            {
1154                if(my @InvokedBy = sort keys(%{$MethodUsed{2}{$AddedMethod}}))
1155                {
1156                    %{$SubProblems{"Abstract_Class_Added_Abstract_Method_Invoked_By_Others"}{getSFormat($AddedMethod)}} = (
1157                        "Type_Name"=>$Type1{"Name"},
1158                        "Type_Type"=>$Type1{"Type"},
1159                        "Target"=>$AddedMethod,
1160                        "Invoked_By"=>$InvokedBy[0]);
1161                }
1162                else
1163                {
1164                    %{$SubProblems{"Abstract_Class_Added_Abstract_Method"}{getSFormat($AddedMethod)}} = (
1165                        "Type_Name"=>$Type1{"Name"},
1166                        "Type_Type"=>$Type1{"Type"},
1167                        "Target"=>$AddedMethod);
1168                }
1169            }
1170            else
1171            {
1172                %{$SubProblems{"NonAbstract_Class_Added_Abstract_Method"}{getSFormat($AddedMethod)}} = (
1173                    "Type_Name"=>$Type1{"Name"},
1174                    "Type_Type"=>$Type1{"Type"},
1175                    "Target"=>$AddedMethod);
1176            }
1177        }
1178        else
1179        {
1180            if(nonImplClass(\%Type1))
1181            {
1182                %{$SubProblems{"NonImpl_Interface_Added_Abstract_Method"}{getSFormat($AddedMethod)}} = (
1183                    "Type_Name"=>$Type1{"Name"},
1184                    "Type_Type"=>$Type1{"Type"},
1185                    "Target"=>$AddedMethod);
1186            }
1187            elsif(my @InvokedBy = sort keys(%{$MethodUsed{2}{$AddedMethod}}))
1188            {
1189                %{$SubProblems{"Interface_Added_Abstract_Method_Invoked_By_Others"}{getSFormat($AddedMethod)}} = (
1190                    "Type_Name"=>$Type1{"Name"},
1191                    "Type_Type"=>$Type1{"Type"},
1192                    "Target"=>$AddedMethod,
1193                    "Invoked_By"=>$InvokedBy[0]);
1194            }
1195            else
1196            {
1197                %{$SubProblems{"Interface_Added_Abstract_Method"}{getSFormat($AddedMethod)}} = (
1198                    "Type_Name"=>$Type1{"Name"},
1199                    "Type_Type"=>$Type1{"Type"},
1200                    "Target"=>$AddedMethod);
1201            }
1202        }
1203    }
1204    foreach my $RemovedMethod (keys(%{$RemovedMethod_Abstract{$Type1{"Name"}}}))
1205    {
1206        if($Type1{"Type"} eq "class")
1207        {
1208            %{$SubProblems{"Class_Removed_Abstract_Method"}{getSFormat($RemovedMethod)}} = (
1209                "Type_Name"=>$Type1{"Name"},
1210                "Type_Type"=>$Type1{"Type"},
1211                "Target"=>$RemovedMethod);
1212        }
1213        else
1214        {
1215            %{$SubProblems{"Interface_Removed_Abstract_Method"}{getSFormat($RemovedMethod)}} = (
1216                "Type_Name"=>$Type1{"Name"},
1217                "Type_Type"=>$Type1{"Type"},
1218                "Target"=>$RemovedMethod);
1219        }
1220    }
1221    if($Type1{"Type"} eq "class"
1222    and $Type2{"Type"} eq "class")
1223    {
1224        my $SuperClass1 = getType($Type1{"SuperClass"}, 1);
1225        my $SuperClass2 = getType($Type2{"SuperClass"}, 2);
1226       
1227        my $SuperClassName1 = $SuperClass1->{"Name"};
1228        my $SuperClassName2 = $SuperClass2->{"Name"};
1229       
1230        # Java 6: java.lang.Object
1231        # Java 7: none
1232        if(not $SuperClassName1) {
1233            $SuperClassName1 = "java.lang.Object";
1234        }
1235       
1236        if(not $SuperClassName2) {
1237            $SuperClassName2 = "java.lang.Object";
1238        }
1239       
1240        if($SuperClassName2 ne $SuperClassName1)
1241        {
1242            if($SuperClassName1 eq "java.lang.Object")
1243            {
1244                if($SuperClass2->{"Abstract"}
1245                and $Type1{"Abstract"} and $Type2{"Abstract"}
1246                and keys(%{$Class_AbstractMethods{2}{$SuperClassName2}}))
1247                {
1248                    if(my ($Invoked, $InvokedBy) = getInvoked($Type1{"Name"}))
1249                    {
1250                        %{$SubProblems{"Abstract_Class_Added_Super_Abstract_Class_Invoked_By_Others"}{""}} = (
1251                            "Type_Name"=>$Type1{"Name"},
1252                            "Target"=>$SuperClassName2,
1253                            "Invoked"=>$Invoked,
1254                            "Invoked_By"=>$InvokedBy);
1255                    }
1256                    else
1257                    {
1258                        %{$SubProblems{"Abstract_Class_Added_Super_Abstract_Class"}{""}} = (
1259                            "Type_Name"=>$Type1{"Name"},
1260                            "Target"=>$SuperClassName2);
1261                    }
1262                }
1263                else
1264                {
1265                    %{$SubProblems{"Added_Super_Class"}{""}} = (
1266                        "Type_Name"=>$Type1{"Name"},
1267                        "Target"=>$SuperClassName2);
1268                }
1269            }
1270            elsif($SuperClassName2 eq "java.lang.Object")
1271            {
1272                %{$SubProblems{"Removed_Super_Class"}{""}} = (
1273                    "Type_Name"=>$Type1{"Name"},
1274                    "Target"=>$SuperClassName1);
1275            }
1276            else
1277            {
1278                %{$SubProblems{"Changed_Super_Class"}{""}} = (
1279                    "Type_Name"=>$Type1{"Name"},
1280                    "Target"=>$SuperClassName1,
1281                    "Old_Value"=>$SuperClassName1,
1282                    "New_Value"=>$SuperClassName2);
1283            }
1284        }
1285    }
1286    my %SuperInterfaces_Old = map {getTypeName($_, 1) => 1} keys(%{$Type1{"SuperInterface"}});
1287    my %SuperInterfaces_New = map {getTypeName($_, 2) => 1} keys(%{$Type2{"SuperInterface"}});
1288    foreach my $SuperInterface (keys(%SuperInterfaces_New))
1289    {
1290        if(not $SuperInterfaces_Old{$SuperInterface})
1291        {
1292            my $HaveMethods = keys(%{$Class_AbstractMethods{2}{$SuperInterface}});
1293            my $HaveFields = keys(%{$Class_Fields{2}{$SuperInterface}});
1294           
1295            if($Type1{"Type"} eq "interface")
1296            {
1297                if($HaveMethods
1298                or $SuperInterface=~/\Ajava\./)
1299                {
1300                    if($HaveMethods and checkDefaultImpl(2, $SuperInterface, $Type2{"Name"}))
1301                    {
1302                        %{$SubProblems{"Interface_Added_Super_Interface_With_Implemented_Methods"}{getSFormat($SuperInterface)}} = (
1303                            "Type_Name"=>$Type1{"Name"},
1304                            "Target"=>$SuperInterface);
1305                    }
1306                    else
1307                    {
1308                        if(my ($Invoked, $InvokedBy) = getInvoked($Type1{"Name"}))
1309                        {
1310                            %{$SubProblems{"Interface_Added_Super_Interface_Used_By_Others"}{getSFormat($SuperInterface)}} = (
1311                                "Type_Name"=>$Type1{"Name"},
1312                                "Target"=>$SuperInterface,
1313                                "Invoked"=>$Invoked,
1314                                "Invoked_By"=>$InvokedBy);
1315                        }
1316                        else
1317                        {
1318                            %{$SubProblems{"Interface_Added_Super_Interface"}{getSFormat($SuperInterface)}} = (
1319                                "Type_Name"=>$Type1{"Name"},
1320                                "Target"=>$SuperInterface);
1321                        }
1322                    }
1323                }
1324                elsif($HaveFields)
1325                {
1326                    %{$SubProblems{"Interface_Added_Super_Constant_Interface"}{getSFormat($SuperInterface)}} = (
1327                        "Type_Name"=>$Type2{"Name"},
1328                        "Target"=>$SuperInterface);
1329                }
1330                else
1331                {
1332                    # empty interface
1333                }
1334            }
1335            else
1336            {
1337                if($Type1{"Abstract"} and $Type2{"Abstract"})
1338                {
1339                    if($HaveMethods and checkDefaultImpl(2, $SuperInterface, $Type2{"Name"}))
1340                    {
1341                        %{$SubProblems{"Abstract_Class_Added_Super_Interface_With_Implemented_Methods"}{getSFormat($SuperInterface)}} = (
1342                            "Type_Name"=>$Type1{"Name"},
1343                            "Target"=>$SuperInterface);
1344                    }
1345                    else
1346                    {
1347                        if(my ($Invoked, $InvokedBy) = getInvoked($Type1{"Name"}))
1348                        {
1349                            %{$SubProblems{"Abstract_Class_Added_Super_Interface_Invoked_By_Others"}{getSFormat($SuperInterface)}} = (
1350                                "Type_Name"=>$Type1{"Name"},
1351                                "Target"=>$SuperInterface,
1352                                "Invoked"=>$Invoked,
1353                                "Invoked_By"=>$InvokedBy);
1354                        }
1355                        else
1356                        {
1357                            %{$SubProblems{"Abstract_Class_Added_Super_Interface"}{getSFormat($SuperInterface)}} = (
1358                                "Type_Name"=>$Type1{"Name"},
1359                                "Target"=>$SuperInterface);
1360                        }
1361                    }
1362                }
1363            }
1364        }
1365    }
1366    foreach my $SuperInterface (keys(%SuperInterfaces_Old))
1367    {
1368        if(not $SuperInterfaces_New{$SuperInterface})
1369        {
1370            my $HaveMethods = keys(%{$Class_AbstractMethods{1}{$SuperInterface}});
1371            my $HaveFields = keys(%{$Class_Fields{1}{$SuperInterface}});
1372           
1373            if($Type1{"Type"} eq "interface")
1374            {
1375                if($HaveMethods
1376                or $SuperInterface=~/\Ajava\./)
1377                {
1378                    %{$SubProblems{"Interface_Removed_Super_Interface"}{getSFormat($SuperInterface)}} = (
1379                        "Type_Name"=>$Type1{"Name"},
1380                        "Type_Type"=>"interface",
1381                        "Target"=>$SuperInterface);
1382                }
1383                elsif($HaveFields)
1384                {
1385                    %{$SubProblems{"Interface_Removed_Super_Constant_Interface"}{getSFormat($SuperInterface)}} = (
1386                        "Type_Name"=>$Type1{"Name"},
1387                        "Target"=>$SuperInterface);
1388                }
1389                else {
1390                    # empty interface
1391                }
1392            }
1393            else
1394            {
1395                %{$SubProblems{"Class_Removed_Super_Interface"}{getSFormat($SuperInterface)}} = (
1396                    "Type_Name"=>$Type1{"Name"},
1397                    "Type_Type"=>"class",
1398                    "Target"=>$SuperInterface);
1399            }
1400        }
1401    }
1402   
1403    foreach my $Field_Name (sort keys(%{$Type1{"Fields"}}))
1404    {# check older fields
1405        my $Access1 = $Type1{"Fields"}{$Field_Name}{"Access"};
1406        if($Access1=~/private/) {
1407            next;
1408        }
1409       
1410        my $Field_Pos1 = $Type1{"Fields"}{$Field_Name}{"Pos"};
1411        my $FieldType1_Id = $Type1{"Fields"}{$Field_Name}{"Type"};
1412        my $FieldType1_Name = getTypeName($FieldType1_Id, 1);
1413       
1414        if(not $Type2{"Fields"}{$Field_Name})
1415        {# removed fields
1416            my $StraightPair_Name = findFieldPair($Field_Pos1, \%Type2);
1417            if($StraightPair_Name ne "lost" and not $Type1{"Fields"}{$StraightPair_Name}
1418            and $FieldType1_Name eq getTypeName($Type2{"Fields"}{$StraightPair_Name}{"Type"}, 2))
1419            {
1420                if(my $Constant = getConstantValue($Type1{"Fields"}{$Field_Name}{"Value"}, $FieldType1_Name))
1421                {
1422                    %{$SubProblems{"Renamed_Constant_Field"}{$Field_Name}}=(
1423                        "Target"=>$Field_Name,
1424                        "Type_Name"=>$Type1{"Name"},
1425                        "Old_Value"=>$Field_Name,
1426                        "New_Value"=>$StraightPair_Name,
1427                        "Field_Type"=>$FieldType1_Name,
1428                        "Field_Value"=>$Constant);
1429                }
1430                else
1431                {
1432                    %{$SubProblems{"Renamed_Field"}{$Field_Name}}=(
1433                        "Target"=>$Field_Name,
1434                        "Type_Name"=>$Type1{"Name"},
1435                        "Old_Value"=>$Field_Name,
1436                        "New_Value"=>$StraightPair_Name,
1437                        "Field_Type"=>$FieldType1_Name);
1438                }
1439            }
1440            else
1441            {
1442                if(my $Constant = getConstantValue($Type1{"Fields"}{$Field_Name}{"Value"}, $FieldType1_Name))
1443                { # has a compile-time constant value
1444                    %{$SubProblems{"Removed_Constant_Field"}{$Field_Name}}=(
1445                        "Target"=>$Field_Name,
1446                        "Type_Name"=>$Type1{"Name"},
1447                        "Field_Value"=>$Constant,
1448                        "Field_Type"=>$FieldType1_Name,
1449                        "Type_Type"=>$Type1{"Type"});
1450                }
1451                else
1452                {
1453                    %{$SubProblems{"Removed_NonConstant_Field"}{$Field_Name}}=(
1454                        "Target"=>$Field_Name,
1455                        "Type_Name"=>$Type1{"Name"},
1456                        "Type_Type"=>$Type1{"Type"},
1457                        "Field_Type"=>$FieldType1_Name);
1458                }
1459            }
1460            next;
1461        }
1462       
1463        my $FieldType2_Id = $Type2{"Fields"}{$Field_Name}{"Type"};
1464        my $FieldType2_Name = getTypeName($FieldType2_Id, 2);
1465       
1466        if(not $Type1{"Fields"}{$Field_Name}{"Static"}
1467        and $Type2{"Fields"}{$Field_Name}{"Static"})
1468        {
1469            if(not $Type1{"Fields"}{$Field_Name}{"Value"})
1470            {
1471                %{$SubProblems{"NonConstant_Field_Became_Static"}{$Field_Name}}=(
1472                    "Target"=>$Field_Name,
1473                    "Field_Type"=>$FieldType1_Name,
1474                    "Type_Name"=>$Type1{"Name"});
1475            }
1476        }
1477        elsif($Type1{"Fields"}{$Field_Name}{"Static"}
1478        and not $Type2{"Fields"}{$Field_Name}{"Static"})
1479        {
1480            if($Type1{"Fields"}{$Field_Name}{"Value"})
1481            {
1482                %{$SubProblems{"Constant_Field_Became_NonStatic"}{$Field_Name}}=(
1483                    "Target"=>$Field_Name,
1484                    "Field_Type"=>$FieldType1_Name,
1485                    "Type_Name"=>$Type1{"Name"});
1486            }
1487            else
1488            {
1489                %{$SubProblems{"NonConstant_Field_Became_NonStatic"}{$Field_Name}}=(
1490                    "Target"=>$Field_Name,
1491                    "Field_Type"=>$FieldType1_Name,
1492                    "Type_Name"=>$Type1{"Name"});
1493            }
1494        }
1495        if(not $Type1{"Fields"}{$Field_Name}{"Final"}
1496        and $Type2{"Fields"}{$Field_Name}{"Final"})
1497        {
1498            %{$SubProblems{"Field_Became_Final"}{$Field_Name}}=(
1499                "Target"=>$Field_Name,
1500                "Field_Type"=>$FieldType1_Name,
1501                "Type_Name"=>$Type1{"Name"});
1502        }
1503        elsif($Type1{"Fields"}{$Field_Name}{"Final"}
1504        and not $Type2{"Fields"}{$Field_Name}{"Final"})
1505        {
1506            %{$SubProblems{"Field_Became_NonFinal"}{$Field_Name}}=(
1507                "Target"=>$Field_Name,
1508                "Field_Type"=>$FieldType1_Name,
1509                "Type_Name"=>$Type1{"Name"});
1510        }
1511        my $Access2 = $Type2{"Fields"}{$Field_Name}{"Access"};
1512        if($Access1 eq "public" and $Access2=~/protected|private/
1513        or $Access1 eq "protected" and $Access2=~/private/)
1514        {
1515            if($Access2 eq "package-private")
1516            {
1517                %{$SubProblems{"Changed_Field_Access_To_Package_Private"}{$Field_Name}}=(
1518                    "Target"=>$Field_Name,
1519                    "Type_Name"=>$Type1{"Name"},
1520                    "Old_Value"=>$Access1,
1521                    "New_Value"=>$Access2);
1522            }
1523            else
1524            {
1525                %{$SubProblems{"Changed_Field_Access"}{$Field_Name}}=(
1526                    "Target"=>$Field_Name,
1527                    "Type_Name"=>$Type1{"Name"},
1528                    "Old_Value"=>$Access1,
1529                    "New_Value"=>$Access2);
1530            }
1531        }
1532       
1533        my $Value1 = getConstantValue($Type1{"Fields"}{$Field_Name}{"Value"}, $FieldType1_Name);
1534        my $Value2 = getConstantValue($Type2{"Fields"}{$Field_Name}{"Value"}, $FieldType2_Name);
1535       
1536        if($Value1 ne $Value2)
1537        {
1538            if($Value1 and $Value2)
1539            {
1540                if($Type1{"Fields"}{$Field_Name}{"Final"}
1541                and $Type2{"Fields"}{$Field_Name}{"Final"})
1542                {
1543                    if($Field_Name=~/(\A|_)(VERSION|VERNUM|BUILDNUMBER|BUILD)(_|\Z)/i)
1544                    {
1545                        %{$SubProblems{"Changed_Final_Version_Field_Value"}{$Field_Name}}=(
1546                            "Target"=>$Field_Name,
1547                            "Field_Type"=>$FieldType1_Name,
1548                            "Type_Name"=>$Type1{"Name"},
1549                            "Old_Value"=>$Value1,
1550                            "New_Value"=>$Value2);
1551                    }
1552                    else
1553                    {
1554                        %{$SubProblems{"Changed_Final_Field_Value"}{$Field_Name}}=(
1555                            "Target"=>$Field_Name,
1556                            "Field_Type"=>$FieldType1_Name,
1557                            "Type_Name"=>$Type1{"Name"},
1558                            "Old_Value"=>$Value1,
1559                            "New_Value"=>$Value2);
1560                    }
1561                }
1562            }
1563        }
1564       
1565        my %Sub_SubChanges = detectTypeChange(\%Type1, \%Type2, $FieldType1_Id, $FieldType2_Id, "Field");
1566        foreach my $Sub_SubProblemType (keys(%Sub_SubChanges))
1567        {
1568            %{$SubProblems{$Sub_SubProblemType}{$Field_Name}}=(
1569                "Target"=>$Field_Name,
1570                "Type_Name"=>$Type1{"Name"});
1571           
1572            foreach my $Attr (keys(%{$Sub_SubChanges{$Sub_SubProblemType}}))
1573            {
1574                $SubProblems{$Sub_SubProblemType}{$Field_Name}{$Attr} = $Sub_SubChanges{$Sub_SubProblemType}{$Attr};
1575            }
1576        }
1577       
1578        if($FieldType1_Id and $FieldType2_Id)
1579        { # check field type change
1580            my $Sub_SubProblems = mergeTypes($FieldType1_Id, $FieldType2_Id);
1581            my %DupProblems = ();
1582           
1583            foreach my $Sub_SubProblemType (sort keys(%{$Sub_SubProblems}))
1584            {
1585                foreach my $Sub_SubLocation (sort {length($a)<=>length($b)} sort keys(%{$Sub_SubProblems->{$Sub_SubProblemType}}))
1586                {
1587                    if(not defined $In::Opt{"AllAffected"})
1588                    {
1589                        if(defined $DupProblems{$Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation}}) {
1590                            next;
1591                        }
1592                    }
1593                   
1594                    my $NewLocation = ($Sub_SubLocation)?$Field_Name.".".$Sub_SubLocation:$Field_Name;
1595                    $SubProblems{$Sub_SubProblemType}{$NewLocation} = $Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation};
1596                   
1597                    if(not defined $In::Opt{"AllAffected"})
1598                    {
1599                        $DupProblems{$Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation}} = 1;
1600                    }
1601                }
1602            }
1603            %DupProblems = ();
1604        }
1605    }
1606   
1607    foreach my $Field_Name (sort keys(%{$Type2{"Fields"}}))
1608    { # check added fields
1609        if($Type2{"Fields"}{$Field_Name}{"Access"}=~/private/) {
1610            next;
1611        }
1612        my $FieldPos2 = $Type2{"Fields"}{$Field_Name}{"Pos"};
1613        my $FieldType2_Id = $Type2{"Fields"}{$Field_Name}{"Type"};
1614        my $FieldType2_Name = getTypeName($FieldType2_Id, 2);
1615       
1616        if(not $Type1{"Fields"}{$Field_Name})
1617        {# added fields
1618            my $StraightPair_Name = findFieldPair($FieldPos2, \%Type1);
1619            if($StraightPair_Name ne "lost" and not $Type2{"Fields"}{$StraightPair_Name}
1620            and getTypeName($Type1{"Fields"}{$StraightPair_Name}{"Type"}, 1) eq $FieldType2_Name)
1621            {
1622                # Already reported as "Renamed_Field" or "Renamed_Constant_Field"
1623            }
1624            else
1625            {
1626                if($Type1{"Type"} eq "interface")
1627                {
1628                    %{$SubProblems{"Interface_Added_Field"}{$Field_Name}}=(
1629                        "Target"=>$Field_Name,
1630                        "Type_Name"=>$Type1{"Name"});
1631                }
1632                else
1633                {
1634                    %{$SubProblems{"Class_Added_Field"}{$Field_Name}}=(
1635                        "Target"=>$Field_Name,
1636                        "Type_Name"=>$Type1{"Name"});
1637                }
1638            }
1639        }
1640    }
1641   
1642    pop(@RecurTypes);
1643    return ($Cache{"mergeTypes"}{$Type1_Id}{$Type2_Id} = \%SubProblems);
1644}
1645
1646sub checkDefaultImpl($$$)
1647{ # Check if all abstract methods of the super class have
1648  # default implementations in the class
1649    my ($LVer, $SuperClassName, $ClassName) = @_;
1650   
1651    foreach my $Method (keys(%{$Class_AbstractMethods{$LVer}{$SuperClassName}}))
1652    {
1653        if(my $Overridden = findMethod_Class($Method, $ClassName, $LVer))
1654        {
1655            if($MethodInfo{$LVer}{$Overridden}{"Abstract"}) {
1656                return 0;
1657            }
1658        }
1659        else {
1660            return 0;
1661        }
1662    }
1663   
1664    return 1;
1665}
1666
1667sub getMSuffix($)
1668{
1669    my $Method = $_[0];
1670    if($Method=~/(\(.*\))/) {
1671        return $1;
1672    }
1673    return "";
1674}
1675
1676sub getMShort($)
1677{
1678    my $Method = $_[0];
1679    if($Method=~/([^\.]+)\:\(/) {
1680        return $1;
1681    }
1682    return "";
1683}
1684
1685sub findMethod($$$$)
1686{
1687    my ($Method, $MethodVersion, $ClassName, $ClassVersion) = @_;
1688   
1689    my $GenericName = getGeneric($ClassName);
1690    my $ClassId = undef;
1691   
1692    if(defined $TName_Tid{$ClassVersion}{$ClassName}) {
1693        $ClassId = $TName_Tid{$ClassVersion}{$ClassName};
1694    }
1695    elsif(defined $TName_Tid_Generic{$ClassVersion}{$GenericName}) {
1696        $ClassId = $TName_Tid_Generic{$ClassVersion}{$GenericName};
1697    }
1698   
1699    if($ClassId)
1700    {
1701        my @Search = ();
1702        if(getTypeType($ClassId, $ClassVersion) eq "class")
1703        {
1704            if(my $SuperClassId = $TypeInfo{$ClassVersion}{$ClassId}{"SuperClass"}) {
1705                push(@Search, $SuperClassId);
1706            }
1707        }
1708       
1709        if(not defined $MethodInfo{$MethodVersion}{$Method}
1710        or $MethodInfo{$MethodVersion}{$Method}{"Abstract"})
1711        {
1712            if(my @SuperInterfaces = sort keys(%{$TypeInfo{$ClassVersion}{$ClassId}{"SuperInterface"}})) {
1713                push(@Search, @SuperInterfaces);
1714            }
1715        }
1716       
1717        foreach my $SuperId (@Search)
1718        {
1719            if($SuperId eq $ClassId) {
1720                next;
1721            }
1722           
1723            my $SuperName = getTypeName($SuperId, $ClassVersion);
1724           
1725            if(my $MethodInClass = findMethod_Class($Method, $SuperName, $ClassVersion)) {
1726                return $MethodInClass;
1727            }
1728            elsif(my $MethodInSuperClasses = findMethod($Method, $MethodVersion, $SuperName, $ClassVersion)) {
1729                return $MethodInSuperClasses;
1730            }
1731        }
1732    }
1733   
1734    my $TargetSuffix = getMSuffix($Method);
1735    my $TargetShortName = getMShort($Method);
1736   
1737    # search in java.lang.Object
1738    foreach my $C (keys(%JavaObjectMethod))
1739    {
1740        if($TargetSuffix eq getMSuffix($C))
1741        {
1742            if($TargetShortName eq getMShort($C)) {
1743                return $C;
1744            }
1745        }
1746    }
1747   
1748    return undef;
1749}
1750
1751sub findMethod_Class($$$)
1752{
1753    my ($Method, $ClassName, $ClassVersion) = @_;
1754   
1755    my $TargetSuffix = getMSuffix($Method);
1756    my $TargetShortName = getMShort($Method);
1757    my $GenericName = getGeneric($ClassName);
1758   
1759    my @Candidates = ();
1760   
1761    if(defined $Class_Methods{$ClassVersion}{$ClassName}) {
1762        @Candidates = keys(%{$Class_Methods{$ClassVersion}{$ClassName}});
1763    }
1764    elsif(defined $Class_Methods_Generic{$ClassVersion}{$GenericName}) {
1765        @Candidates = keys(%{$Class_Methods_Generic{$ClassVersion}{$GenericName}});
1766    }
1767    else {
1768        return undef;
1769    }
1770   
1771    foreach my $Candidate (sort @Candidates)
1772    { # search for method with the same parameters suffix
1773        next if($MethodInfo{$ClassVersion}{$Candidate}{"Constructor"});
1774       
1775        if($TargetSuffix eq getMSuffix($Candidate))
1776        {
1777            if($TargetShortName eq getMShort($Candidate)) {
1778                return $Candidate;
1779            }
1780        }
1781    }
1782   
1783    return undef;
1784}
1785
1786sub getBaseSignature($)
1787{
1788    my $Method = $_[0];
1789    $Method=~s/\)(.+)\Z/\)/g;
1790    return $Method;
1791}
1792
1793sub prepareData($)
1794{
1795    my $LVer = $_[0];
1796   
1797    if(my $MUsed = $In::API{$LVer}{"MethodUsed"})
1798    {
1799        foreach my $M_Id (keys(%{$MUsed}))
1800        {
1801            my $Name = $MUsed->{$M_Id}{"Name"};
1802            $MethodUsed{$LVer}{$Name} = $MUsed->{$M_Id}{"Used"};
1803        }
1804    }
1805   
1806    foreach my $TypeId (keys(%{$TypeInfo{$LVer}}))
1807    {
1808        my $TypeAttr = $TypeInfo{$LVer}{$TypeId};
1809        my $TName = $TypeAttr->{"Name"};
1810       
1811        $TName_Tid{$LVer}{$TName} = $TypeId;
1812       
1813        if(defined $TypeAttr->{"Archive"})
1814        { # declaration
1815            $TName_Tid_Generic{$LVer}{getGeneric($TName)} = $TypeId;
1816        }
1817       
1818        if(not $TypeAttr->{"Dep"})
1819        {
1820            if(my $Archive = $TypeAttr->{"Archive"}) {
1821                $LibArchives{$LVer}{$Archive} = 1;
1822            }
1823        }
1824       
1825        foreach my $FieldName (keys(%{$TypeAttr->{"Fields"}}))
1826        {
1827            if($TypeAttr->{"Fields"}{$FieldName}{"Access"}=~/public|protected/) {
1828                $Class_Fields{$LVer}{$TName}{$FieldName} = $TypeAttr->{"Fields"}{$FieldName}{"Type"};
1829            }
1830        }
1831    }
1832   
1833    foreach my $Method (keys(%{$MethodInfo{$LVer}}))
1834    {
1835        my $Name = $MethodInfo{$LVer}{$Method}{"Name"};
1836        $MethodInfo{$LVer}{$Name} = delete($MethodInfo{$LVer}{$Method});
1837    }
1838   
1839    foreach my $Method (keys(%{$MethodInfo{$LVer}}))
1840    {
1841        my $MAttr = $MethodInfo{$LVer}{$Method};
1842       
1843        $MAttr->{"Signature"} = getSignature($Method, $LVer, "Full");
1844       
1845        if(my $ClassId = $MAttr->{"Class"}
1846        and $MAttr->{"Access"}=~/public|protected/)
1847        {
1848            my $CName = getTypeName($ClassId, $LVer);
1849            $Class_Methods{$LVer}{$CName}{$Method} = 1;
1850            $Class_Methods_Generic{$LVer}{getGeneric($CName)}{$Method} = 1;
1851           
1852            if($MAttr->{"Abstract"}) {
1853                $Class_AbstractMethods{$LVer}{$CName}{$Method} = 1;
1854            }
1855        }
1856       
1857        if($MAttr->{"Access"}!~/private/)
1858        {
1859            if($MAttr->{"Constructor"}) {
1860                registerUsage($MAttr->{"Class"}, $LVer);
1861            }
1862            else {
1863                registerUsage($MAttr->{"Return"}, $LVer);
1864            }
1865        }
1866       
1867        if($LVer==1 and not $MAttr->{"Constructor"}
1868        and my $BaseSign = getBaseSignature($Method)) {
1869            $OldMethodSignature{$BaseSign} = $Method;
1870        }
1871    }
1872}
1873
1874sub mergeMethods()
1875{
1876    foreach my $Method (sort keys(%{$MethodInfo{1}}))
1877    { # compare methods
1878        next if(not defined $MethodInfo{2}{$Method});
1879       
1880        if(not $MethodInfo{1}{$Method}{"Archive"}
1881        or not $MethodInfo{2}{$Method}{"Archive"}) {
1882            next;
1883        }
1884       
1885        if(not methodFilter($Method, 1)) {
1886            next;
1887        }
1888       
1889        my $ClassId1 = $MethodInfo{1}{$Method}{"Class"};
1890        my $Class1 = getType($ClassId1, 1);
1891       
1892        if(not defined $In::Opt{"CheckAnnotations"} and $Class1->{"Annotation"}) {
1893            next;
1894        }
1895       
1896        my $Class1_Name = $Class1->{"Name"};
1897        my $Class1_Type = $Class1->{"Type"};
1898       
1899        $CheckedTypes{$Class1_Name} = 1;
1900        $CheckedMethods{$Method} = 1;
1901       
1902        if(not $MethodInfo{1}{$Method}{"Static"}
1903        and $Class1_Type eq "class" and not $Class_Constructed{1}{$ClassId1})
1904        { # class cannot be constructed or inherited by clients
1905          # non-static method cannot be called
1906            next;
1907        }
1908       
1909        # checking attributes
1910        if(not $MethodInfo{1}{$Method}{"Static"}
1911        and $MethodInfo{2}{$Method}{"Static"}) {
1912            %{$CompatProblems{$Method}{"Method_Became_Static"}{""}} = ();
1913        }
1914        elsif($MethodInfo{1}{$Method}{"Static"}
1915        and not $MethodInfo{2}{$Method}{"Static"}) {
1916            %{$CompatProblems{$Method}{"Method_Became_NonStatic"}{""}} = ();
1917        }
1918       
1919        if(not $MethodInfo{1}{$Method}{"Synchronized"}
1920        and $MethodInfo{2}{$Method}{"Synchronized"}) {
1921            %{$CompatProblems{$Method}{"Method_Became_Synchronized"}{""}} = ();
1922        }
1923        elsif($MethodInfo{1}{$Method}{"Synchronized"}
1924        and not $MethodInfo{2}{$Method}{"Synchronized"}) {
1925            %{$CompatProblems{$Method}{"Method_Became_NonSynchronized"}{""}} = ();
1926        }
1927       
1928        if(not $MethodInfo{1}{$Method}{"Final"}
1929        and $MethodInfo{2}{$Method}{"Final"})
1930        {
1931            if($MethodInfo{1}{$Method}{"Static"}) {
1932                %{$CompatProblems{$Method}{"Static_Method_Became_Final"}{""}} = ();
1933            }
1934            else {
1935                %{$CompatProblems{$Method}{"NonStatic_Method_Became_Final"}{""}} = ();
1936            }
1937        }
1938       
1939        my $Access1 = $MethodInfo{1}{$Method}{"Access"};
1940        my $Access2 = $MethodInfo{2}{$Method}{"Access"};
1941       
1942        if($Access1 eq "public" and $Access2=~/protected|private/
1943        or $Access1 eq "protected" and $Access2=~/private/)
1944        {
1945            %{$CompatProblems{$Method}{"Changed_Method_Access"}{""}} = (
1946                "Old_Value"=>$Access1,
1947                "New_Value"=>$Access2);
1948        }
1949       
1950        my $Class2_Type = getTypeType($MethodInfo{2}{$Method}{"Class"}, 2);
1951       
1952        if($Class1_Type eq "class"
1953        and $Class2_Type eq "class")
1954        {
1955            if(not $MethodInfo{1}{$Method}{"Abstract"}
1956            and $MethodInfo{2}{$Method}{"Abstract"})
1957            {
1958                %{$CompatProblems{$Method}{"Method_Became_Abstract"}{""}} = ();
1959                %{$CompatProblems{$Method}{"Class_Method_Became_Abstract"}{"this.".getSFormat($Method)}} = (
1960                    "Type_Name"=>$Class1_Name,
1961                    "Target"=>$Method);
1962            }
1963            elsif($MethodInfo{1}{$Method}{"Abstract"}
1964            and not $MethodInfo{2}{$Method}{"Abstract"})
1965            {
1966                %{$CompatProblems{$Method}{"Method_Became_NonAbstract"}{""}} = ();
1967                %{$CompatProblems{$Method}{"Class_Method_Became_NonAbstract"}{"this.".getSFormat($Method)}} = (
1968                    "Type_Name"=>$Class1_Name,
1969                    "Target"=>$Method);
1970            }
1971        }
1972        elsif($Class1_Type eq "interface"
1973        and $Class2_Type eq "interface")
1974        {
1975            if(not $MethodInfo{1}{$Method}{"Abstract"}
1976            and $MethodInfo{2}{$Method}{"Abstract"})
1977            {
1978                %{$CompatProblems{$Method}{"Method_Became_NonDefault"}{""}} = ();
1979                %{$CompatProblems{$Method}{"Interface_Method_Became_NonDefault"}{"this.".getSFormat($Method)}} = (
1980                    "Type_Name"=>$Class1_Name,
1981                    "Target"=>$Method);
1982            }
1983            elsif($MethodInfo{1}{$Method}{"Abstract"}
1984            and not $MethodInfo{2}{$Method}{"Abstract"})
1985            {
1986                %{$CompatProblems{$Method}{"Method_Became_Default"}{""}} = ();
1987                %{$CompatProblems{$Method}{"Interface_Method_Became_Default"}{"this.".getSFormat($Method)}} = (
1988                    "Type_Name"=>$Class1_Name,
1989                    "Target"=>$Method);
1990            }
1991        }
1992       
1993        my %Exceptions_Old = map {getTypeName($_, 1) => $_} keys(%{$MethodInfo{1}{$Method}{"Exceptions"}});
1994        my %Exceptions_New = map {getTypeName($_, 2) => $_} keys(%{$MethodInfo{2}{$Method}{"Exceptions"}});
1995        foreach my $Exception (keys(%Exceptions_Old))
1996        {
1997            if(not $Exceptions_New{$Exception})
1998            {
1999                my $EType = getType($Exceptions_Old{$Exception}, 1);
2000                my $SuperClass = $EType->{"SuperClass"};
2001               
2002                if($KnownRuntimeExceptions{$Exception}
2003                or defined $SuperClass and getTypeName($SuperClass, 1) eq "java.lang.RuntimeException")
2004                {
2005                    if(not $MethodInfo{1}{$Method}{"Abstract"}
2006                    and not $MethodInfo{2}{$Method}{"Abstract"})
2007                    {
2008                        %{$CompatProblems{$Method}{"Removed_Unchecked_Exception"}{"this.".getSFormat($Exception)}} = (
2009                            "Type_Name"=>$Class1_Name,
2010                            "Target"=>$Exception);
2011                    }
2012                }
2013                else
2014                {
2015                    if($MethodInfo{1}{$Method}{"Abstract"}
2016                    and $MethodInfo{2}{$Method}{"Abstract"})
2017                    {
2018                        %{$CompatProblems{$Method}{"Abstract_Method_Removed_Checked_Exception"}{"this.".getSFormat($Exception)}} = (
2019                            "Type_Name"=>$Class1_Name,
2020                            "Target"=>$Exception);
2021                    }
2022                    else
2023                    {
2024                        %{$CompatProblems{$Method}{"NonAbstract_Method_Removed_Checked_Exception"}{"this.".getSFormat($Exception)}} = (
2025                            "Type_Name"=>$Class1_Name,
2026                            "Target"=>$Exception);
2027                    }
2028                }
2029            }
2030        }
2031       
2032        foreach my $Exception (keys(%Exceptions_New))
2033        {
2034            if(not $Exceptions_Old{$Exception})
2035            {
2036                my $EType = getType($Exceptions_New{$Exception}, 2);
2037                my $SuperClass = $EType->{"SuperClass"};
2038               
2039                if($KnownRuntimeExceptions{$Exception}
2040                or defined $SuperClass and getTypeName($SuperClass, 2) eq "java.lang.RuntimeException")
2041                {
2042                    if(not $MethodInfo{1}{$Method}{"Abstract"}
2043                    and not $MethodInfo{2}{$Method}{"Abstract"})
2044                    {
2045                        %{$CompatProblems{$Method}{"Added_Unchecked_Exception"}{"this.".getSFormat($Exception)}} = (
2046                            "Type_Name"=>$Class1_Name,
2047                            "Target"=>$Exception);
2048                    }
2049                }
2050                else
2051                {
2052                    if($MethodInfo{1}{$Method}{"Abstract"}
2053                    and $MethodInfo{2}{$Method}{"Abstract"})
2054                    {
2055                        %{$CompatProblems{$Method}{"Abstract_Method_Added_Checked_Exception"}{"this.".getSFormat($Exception)}} = (
2056                            "Type_Name"=>$Class1_Name,
2057                            "Target"=>$Exception);
2058                    }
2059                    else
2060                    {
2061                        %{$CompatProblems{$Method}{"NonAbstract_Method_Added_Checked_Exception"}{"this.".getSFormat($Exception)}} = (
2062                            "Type_Name"=>$Class1_Name,
2063                            "Target"=>$Exception);
2064                    }
2065                }
2066            }
2067        }
2068       
2069        if(defined $MethodInfo{1}{$Method}{"Param"})
2070        {
2071            foreach my $ParamPos (sort {int($a) <=> int($b)} keys(%{$MethodInfo{1}{$Method}{"Param"}}))
2072            { # checking parameters
2073                mergeParameters($Method, $ParamPos, $ParamPos);
2074            }
2075        }
2076       
2077        # check object type
2078        my $ObjectType1_Id = $MethodInfo{1}{$Method}{"Class"};
2079        my $ObjectType2_Id = $MethodInfo{2}{$Method}{"Class"};
2080        if($ObjectType1_Id and $ObjectType2_Id)
2081        {
2082            my $SubProblems = mergeTypes($ObjectType1_Id, $ObjectType2_Id);
2083            foreach my $SubProblemType (keys(%{$SubProblems}))
2084            {
2085                foreach my $SubLocation (keys(%{$SubProblems->{$SubProblemType}}))
2086                {
2087                    my $NewLocation = ($SubLocation)?"this.".$SubLocation:"this";
2088                    $CompatProblems{$Method}{$SubProblemType}{$NewLocation} = $SubProblems->{$SubProblemType}{$SubLocation};
2089                }
2090            }
2091        }
2092        # check return type
2093        my $ReturnType1_Id = $MethodInfo{1}{$Method}{"Return"};
2094        my $ReturnType2_Id = $MethodInfo{2}{$Method}{"Return"};
2095        if($ReturnType1_Id and $ReturnType2_Id)
2096        {
2097            my $SubProblems = mergeTypes($ReturnType1_Id, $ReturnType2_Id);
2098            foreach my $SubProblemType (keys(%{$SubProblems}))
2099            {
2100                foreach my $SubLocation (keys(%{$SubProblems->{$SubProblemType}}))
2101                {
2102                    my $NewLocation = ($SubLocation)?"retval.".$SubLocation:"retval";
2103                    $CompatProblems{$Method}{$SubProblemType}{$NewLocation} = $SubProblems->{$SubProblemType}{$SubLocation};
2104                }
2105            }
2106        }
2107    }
2108}
2109
2110sub mergeParameters($$$)
2111{
2112    my ($Method, $ParamPos1, $ParamPos2) = @_;
2113    if(not $Method or not defined $MethodInfo{1}{$Method}{"Param"}
2114    or not defined $MethodInfo{2}{$Method}{"Param"}) {
2115        return;
2116    }
2117   
2118    my $ParamType1_Id = $MethodInfo{1}{$Method}{"Param"}{$ParamPos1}{"Type"};
2119    my $ParamType2_Id = $MethodInfo{2}{$Method}{"Param"}{$ParamPos2}{"Type"};
2120   
2121    if(not $ParamType1_Id or not $ParamType2_Id) {
2122        return;
2123    }
2124   
2125    my $Parameter_Name = $MethodInfo{1}{$Method}{"Param"}{$ParamPos1}{"Name"};
2126    my $Parameter_Location = ($Parameter_Name)?$Parameter_Name:showPos($ParamPos1)." Parameter";
2127   
2128    # checking type declaration changes
2129    my $SubProblems = mergeTypes($ParamType1_Id, $ParamType2_Id);
2130   
2131    my $Type1 = getType($ParamType1_Id, 1);
2132    my $Type2 = getType($ParamType2_Id, 2);
2133   
2134    if($Type1->{"Name"} ne $Type2->{"Name"})
2135    {
2136        if(index($Type1->{"Name"}, "...")!=-1 and index($Type2->{"Name"}, "[]")!=-1)
2137        {
2138            %{$CompatProblems{$Method}{"Variable_Arity_To_Array"}{$Parameter_Name}} = (
2139                "Type_Name"=>getTypeName($MethodInfo{1}{$Method}{"Class"}, 1),
2140                "Param_Name"=>$Parameter_Name,
2141                "Old_Value"=>$Type1->{"Name"},
2142                "New_Value"=>$Type2->{"Name"});
2143        }
2144        elsif(index($Type1->{"Name"}, "[]")!=-1 and index($Type2->{"Name"}, "...")!=-1)
2145        {
2146            %{$CompatProblems{$Method}{"Array_To_Variable_Arity"}{$Parameter_Name}} = (
2147                "Type_Name"=>getTypeName($MethodInfo{1}{$Method}{"Class"}, 1),
2148                "Param_Name"=>$Parameter_Name,
2149                "Old_Value"=>$Type1->{"Name"},
2150                "New_Value"=>$Type2->{"Name"});
2151        }
2152    }
2153   
2154    foreach my $SubProblemType (keys(%{$SubProblems}))
2155    {
2156        foreach my $SubLocation (keys(%{$SubProblems->{$SubProblemType}}))
2157        {
2158            my $NewLocation = ($SubLocation)?$Parameter_Location.".".$SubLocation:$Parameter_Location;
2159            $CompatProblems{$Method}{$SubProblemType}{$NewLocation} = $SubProblems->{$SubProblemType}{$SubLocation};
2160        }
2161    }
2162}
2163
2164sub detectTypeChange($$$$$)
2165{
2166    my ($Ct1, $Ct2, $Type1_Id, $Type2_Id, $Prefix) = @_;
2167    my %LocalProblems = ();
2168   
2169    my $Type1 = getType($Type1_Id, 1);
2170    my $Type2 = getType($Type2_Id, 2);
2171   
2172    my $Type1_Name = $Type1->{"Name"};
2173    my $Type2_Name = $Type2->{"Name"};
2174   
2175    my $Type1_Show = $Type1_Name;
2176    my $Type2_Show = $Type2_Name;
2177   
2178    if(defined $Ct1->{"GenericParam"}
2179    and defined $Ct1->{"GenericParam"}{$Type1_Name})
2180    {
2181        $Type1_Name = getTypeName($Ct1->{"GenericParam"}{$Type1_Name}, 1);
2182        $Type1_Show .= " extends ".$Type1_Name;
2183    }
2184   
2185    if(defined $Ct2->{"GenericParam"}
2186    and defined $Ct2->{"GenericParam"}{$Type2_Name})
2187    {
2188        $Type2_Name = getTypeName($Ct2->{"GenericParam"}{$Type2_Name}, 2);
2189        $Type2_Show .= " extends ".$Type2_Name;
2190    }
2191   
2192    my $Type1_Base = undef;
2193    my $Type2_Base = undef;
2194   
2195    if($Type1->{"Type"} eq "array") {
2196        $Type1_Base = getOneStepBaseType($Type1_Id, 1);
2197    }
2198    else {
2199        $Type1_Base = getBaseType($Type1_Id, 1);
2200    }
2201   
2202    if($Type2->{"Type"} eq "array") {
2203        $Type2_Base = getOneStepBaseType($Type2_Id, 2);
2204    }
2205    else {
2206        $Type2_Base = getBaseType($Type2_Id, 2);
2207    }
2208   
2209    return () if(not $Type1_Name or not $Type2_Name);
2210    return () if(not $Type1_Base->{"Name"} or not $Type2_Base->{"Name"});
2211   
2212    if($Type1_Base->{"Name"} ne $Type2_Base->{"Name"} and $Type1_Name eq $Type2_Name)
2213    {# base type change
2214        %{$LocalProblems{"Changed_".$Prefix."_BaseType"}}=(
2215            "Old_Value"=>$Type1_Base->{"Name"},
2216            "New_Value"=>$Type2_Base->{"Name"});
2217    }
2218    elsif($Type1_Name ne $Type2_Name)
2219    {# type change
2220        %{$LocalProblems{"Changed_".$Prefix."_Type"}}=(
2221            "Old_Value"=>$Type1_Show,
2222            "New_Value"=>$Type2_Show);
2223    }
2224    return %LocalProblems;
2225}
2226
2227sub specChars($)
2228{
2229    my $Str = $_[0];
2230    if(not defined $Str
2231    or $Str eq "") {
2232        return "";
2233    }
2234    $Str=~s/\&([^#]|\Z)/&amp;$1/g;
2235    $Str=~s/</&lt;/g;
2236    $Str=~s/\-\>/&#45;&gt;/g; # &minus;
2237    $Str=~s/>/&gt;/g;
2238    $Str=~s/([^ ])( )([^ ])/$1\@ALONE_SP\@$3/g;
2239    $Str=~s/ /&#160;/g; # &nbsp;
2240    $Str=~s/\@ALONE_SP\@/ /g;
2241    $Str=~s/\n/<br\/>/g;
2242    $Str=~s/\"/&quot;/g;
2243    $Str=~s/\'/&#39;/g;
2244    return $Str;
2245}
2246
2247sub blackName($)
2248{
2249    my $N = $_[0];
2250    return "<span class='iname_b'>".$N."</span>";
2251}
2252
2253sub highLight_ItalicColor($$)
2254{
2255    my ($M, $V) = @_;
2256    return getSignature($M, $V, "Full|HTML|Italic|Color");
2257}
2258
2259sub getSignature($$$)
2260{
2261    my ($Method, $LVer, $Kind) = @_;
2262    if(defined $Cache{"getSignature"}{$LVer}{$Method}{$Kind}) {
2263        return $Cache{"getSignature"}{$LVer}{$Method}{$Kind};
2264    }
2265   
2266    # settings
2267    my ($Full, $Html, $Simple, $Italic, $Color,
2268    $ShowParams, $ShowClass, $ShowAttr, $Desc,
2269    $ShowReturn, $Target) = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, undef);
2270   
2271    if($Kind=~/Full/) {
2272        $Full = 1;
2273    }
2274    if($Kind=~/HTML/) {
2275        $Html = 1;
2276    }
2277    if($Kind=~/Simple/) {
2278        $Simple = 1;
2279    }
2280    if($Kind=~/Italic/) {
2281        $Italic = 1;
2282    }
2283    if($Kind=~/Color/) {
2284        $Color = 1;
2285    }
2286    if($Kind=~/Target=(\d+)/) {
2287        $Target = $1;
2288    }
2289    if($Kind=~/Param/) {
2290        $ShowParams = 1;
2291    }
2292    if($Kind=~/Class/) {
2293        $ShowClass = 1;
2294    }
2295    if($Kind=~/Attr/) {
2296        $ShowAttr = 1;
2297    }
2298    if($Kind=~/Desc/) {
2299        $Desc = 1;
2300    }
2301    if($Kind=~/Return/) {
2302        $ShowReturn = 1;
2303    }
2304   
2305    if(not defined $MethodInfo{$LVer}{$Method}{"ShortName"})
2306    { # from java.lang.Object
2307        if($Html) {
2308            return specChars($Method);
2309        }
2310        else {
2311            return $Method;
2312        }
2313    }
2314   
2315    my $Signature = "";
2316   
2317    my $ShortName = $MethodInfo{$LVer}{$Method}{"ShortName"};
2318   
2319    if($Html) {
2320        $ShortName = specChars($ShortName);
2321    }
2322   
2323    $Signature .= $ShortName;
2324   
2325    if($Full or $ShowClass)
2326    {
2327        my $Class = getTypeName($MethodInfo{$LVer}{$Method}{"Class"}, $LVer);
2328       
2329        if($In::Opt{"HideTemplates"}) {
2330            $Class=~s/<.*>//g;
2331        }
2332       
2333        if($Html) {
2334            $Class = specChars($Class);
2335        }
2336       
2337        $Signature = $Class.".".$Signature;
2338    }
2339    my @Params = ();
2340   
2341    if(defined $MethodInfo{$LVer}{$Method}{"Param"})
2342    {
2343        foreach my $PPos (sort {int($a)<=>int($b)}
2344        keys(%{$MethodInfo{$LVer}{$Method}{"Param"}}))
2345        {
2346            my $PTid = $MethodInfo{$LVer}{$Method}{"Param"}{$PPos}{"Type"};
2347            if(my $PTName = getTypeName($PTid, $LVer))
2348            {
2349                if($In::Opt{"HideTemplates"}) {
2350                    $PTName=~s/<.*>//g;
2351                }
2352               
2353                if(not $In::Opt{"ShowPackages"}) {
2354                    $PTName=~s/(\A|\<\s*|\,\s*)[a-z0-9\.]+\./$1/g;
2355                }
2356               
2357                if($Html) {
2358                    $PTName = specChars($PTName);
2359                }
2360               
2361                if($Full or $ShowParams)
2362                {
2363                    my $PName = $MethodInfo{$LVer}{$Method}{"Param"}{$PPos}{"Name"};
2364                   
2365                    if($Simple) {
2366                        $PName = "<i>$PName</i>";
2367                    }
2368                    elsif($Html)
2369                    {
2370                        my $Style = "param";
2371                       
2372                        if(defined $Target
2373                        and $Target==$PPos) {
2374                            $PName = "<span class='focus_p'>$PName</span>";
2375                        }
2376                        elsif($Color) {
2377                            $PName = "<span class='color_p'>$PName</span>";
2378                        }
2379                        else {
2380                            $PName = "<i>$PName</i>";
2381                        }
2382                    }
2383                   
2384                    push(@Params, $PTName." ".$PName);
2385                }
2386                else {
2387                    push(@Params, $PTName);
2388                }
2389            }
2390        }
2391    }
2392   
2393    if($Simple) {
2394        $Signature = "<b>".$Signature."</b>";
2395    }
2396   
2397    if($Html and not $Simple)
2398    {
2399        $Signature .= "&#160;";
2400        if($Desc) {
2401            $Signature .= "<span class='sym_pd'>";
2402        }
2403        else {
2404            $Signature .= "<span class='sym_p'>";
2405        }
2406        if(@Params)
2407        {
2408            foreach my $Pos (0 .. $#Params)
2409            {
2410                my $Name = "";
2411               
2412                if($Pos==0) {
2413                    $Name .= "(&#160;";
2414                }
2415               
2416                $Name .= $Params[$Pos];
2417               
2418                $Name = "<span>".$Name."</span>";
2419               
2420                if($Pos==$#Params) {
2421                    $Name .= "&#160;)";
2422                }
2423                else {
2424                    $Name .= ", ";
2425                }
2426               
2427                $Signature .= $Name;
2428            }
2429        }
2430        else {
2431            $Signature .= "(&#160;)";
2432        }
2433        $Signature .= "</span>";
2434    }
2435    else
2436    {
2437        if(@Params) {
2438            $Signature .= " ( ".join(", ", @Params)." )";
2439        }
2440        else {
2441            $Signature .= " ( )";
2442        }
2443    }
2444   
2445    if($Full or $ShowAttr)
2446    {
2447        if($MethodInfo{$LVer}{$Method}{"Static"}) {
2448            $Signature .= " [static]";
2449        }
2450        elsif($MethodInfo{$LVer}{$Method}{"Abstract"}) {
2451            $Signature .= " [abstract]";
2452        }
2453    }
2454   
2455    if($Full)
2456    {
2457        if($In::Opt{"ShowAccess"})
2458        {
2459            if(my $Access = $MethodInfo{$LVer}{$Method}{"Access"})
2460            {
2461                if($Access ne "public") {
2462                    $Signature .= " [".$Access."]";
2463                }
2464            }
2465        }
2466    }
2467   
2468    if($Full or $ShowReturn)
2469    {
2470        if(my $ReturnId = $MethodInfo{$LVer}{$Method}{"Return"})
2471        {
2472            my $RName = getTypeName($ReturnId, $LVer);
2473           
2474            if($In::Opt{"HideTemplates"}) {
2475                $RName=~s/<.*>//g;
2476            }
2477           
2478            if(not $In::Opt{"ShowPackages"}) {
2479                $RName=~s/(\A|\<\s*|\,\s*)[a-z0-9\.]+\./$1/g;
2480            }
2481           
2482            if($Desc) {
2483                $Signature = "<b>".specChars($RName)."</b> ".$Signature;
2484            }
2485            elsif($Simple) {
2486                $Signature .= " <b>:</b> ".specChars($RName);
2487            }
2488            elsif($Html) {
2489                $Signature .= "<span class='sym_p nowrap'> &#160;<b>:</b>&#160;&#160;".specChars($RName)."</span>";
2490            }
2491            else {
2492                $Signature .= " : ".$RName;
2493            }
2494        }
2495    }
2496   
2497    if($Full)
2498    {
2499        if(not $In::Opt{"SkipDeprecated"})
2500        {
2501            if($MethodInfo{$LVer}{$Method}{"Deprecated"}) {
2502                $Signature .= " *DEPRECATED*";
2503            }
2504        }
2505    }
2506   
2507    $Signature=~s/java\.lang\.//g;
2508   
2509    if($Html)
2510    {
2511        if(not $In::Opt{"SkipDeprecated"}) {
2512            $Signature=~s!(\*deprecated\*)!<span class='deprecated'>$1</span>!ig;
2513        }
2514       
2515        $Signature=~s!(\[static\]|\[abstract\]|\[public\]|\[private\]|\[protected\])!<span class='attr'>$1</span>!g;
2516    }
2517   
2518    if($Simple) {
2519        $Signature=~s/\[\]/\[ \]/g;
2520    }
2521    elsif($Html)
2522    {
2523        $Signature=~s!\[\]![&#160;]!g;
2524        $Signature=~s!operator=!operator&#160;=!g;
2525    }
2526   
2527    return ($Cache{"getSignature"}{$LVer}{$Method}{$Kind} = $Signature);
2528}
2529
2530sub getReportHeader($)
2531{
2532    my $Level = $_[0];
2533    my $Report_Header = "<h1>";
2534    if($Level eq "Source") {
2535        $Report_Header .= "Source compatibility";
2536    }
2537    elsif($Level eq "Binary") {
2538        $Report_Header .= "Binary compatibility";
2539    }
2540    else {
2541        $Report_Header .= "API compatibility";
2542    }
2543    $Report_Header .= " report for the <span style='color:Blue;'>".$In::Opt{"TargetTitle"}."</span> library between <span style='color:Red;'>".$In::Desc{1}{"Version"}."</span> and <span style='color:Red;'>".$In::Desc{2}{"Version"}."</span> versions";
2544    if($In::Opt{"ClientPath"}) {
2545        $Report_Header .= " (concerning portability of the client: <span style='color:Blue;'>".getFilename($In::Opt{"ClientPath"})."</span>)";
2546    }
2547    $Report_Header .= "</h1>\n";
2548    return $Report_Header;
2549}
2550
2551sub getSourceInfo()
2552{
2553    my $CheckedArchives = "<a name='Checked_Archives'></a>";
2554    if($In::Opt{"OldStyle"}) {
2555        $CheckedArchives .= "<h2>Java Archives (".keys(%{$LibArchives{1}}).")</h2>";
2556    }
2557    else {
2558        $CheckedArchives .= "<h2>Java Archives <span class='gray'>&nbsp;".keys(%{$LibArchives{1}})."&nbsp;</span></h2>";
2559    }
2560    $CheckedArchives .= "\n<hr/><div class='jar_list'>\n";
2561    foreach my $ArchivePath (sort {lc($a) cmp lc($b)}  keys(%{$LibArchives{1}})) {
2562        $CheckedArchives .= getFilename($ArchivePath)."<br/>\n";
2563    }
2564    $CheckedArchives .= "</div><br/>$TOP_REF<br/>\n";
2565    return $CheckedArchives;
2566}
2567
2568sub getTypeProblemsCount($$)
2569{
2570    my ($TargetSeverity, $Level) = @_;
2571    my $Type_Problems_Count = 0;
2572   
2573    foreach my $Type_Name (sort keys(%{$TypeChanges{$Level}}))
2574    {
2575        my %Kinds_Target = ();
2576        foreach my $Kind (sort keys(%{$TypeChanges{$Level}{$Type_Name}}))
2577        {
2578            if($CompatRules{$Level}{$Kind}{"Severity"} ne $TargetSeverity) {
2579                next;
2580            }
2581           
2582            foreach my $Location (sort keys(%{$TypeChanges{$Level}{$Type_Name}{$Kind}}))
2583            {
2584                my $Target = $TypeChanges{$Level}{$Type_Name}{$Kind}{$Location}{"Target"};
2585               
2586                if($Kinds_Target{$Kind}{$Target}) {
2587                    next;
2588                }
2589               
2590                $Kinds_Target{$Kind}{$Target} = 1;
2591                $Type_Problems_Count += 1;
2592            }
2593        }
2594    }
2595   
2596    return $Type_Problems_Count;
2597}
2598
2599sub showNum($)
2600{
2601    if($_[0])
2602    {
2603        my $Num = cutNum($_[0], 2, 0);
2604        if($Num eq "0")
2605        {
2606            foreach my $P (3 .. 7)
2607            {
2608                $Num = cutNum($_[0], $P, 1);
2609                if($Num ne "0") {
2610                    last;
2611                }
2612            }
2613        }
2614        if($Num eq "0") {
2615            $Num = $_[0];
2616        }
2617        return $Num;
2618    }
2619    return $_[0];
2620}
2621
2622sub cutNum($$$)
2623{
2624    my ($num, $digs_to_cut, $z) = @_;
2625    if($num!~/\./)
2626    {
2627        $num .= ".";
2628        foreach (1 .. $digs_to_cut-1) {
2629            $num .= "0";
2630        }
2631    }
2632    elsif($num=~/\.(.+)\Z/ and length($1)<$digs_to_cut-1)
2633    {
2634        foreach (1 .. $digs_to_cut - 1 - length($1)) {
2635            $num .= "0";
2636        }
2637    }
2638    elsif($num=~/\d+\.(\d){$digs_to_cut,}/) {
2639      $num=sprintf("%.".($digs_to_cut-1)."f", $num);
2640    }
2641    $num=~s/\.[0]+\Z//g;
2642    if($z) {
2643        $num=~s/(\.[1-9]+)[0]+\Z/$1/g;
2644    }
2645    return $num;
2646}
2647
2648sub getSummary($)
2649{
2650    my $Level = $_[0];
2651    my ($Added, $Removed, $M_Problems_High, $M_Problems_Medium, $M_Problems_Low,
2652    $T_Problems_High, $T_Problems_Medium, $T_Problems_Low, $M_Other, $T_Other) = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2653   
2654    %{$RESULT{$Level}} = (
2655        "Problems"=>0,
2656        "Warnings"=>0,
2657        "Affected"=>0);
2658   
2659    # check rules
2660    foreach my $Method (sort keys(%CompatProblems))
2661    {
2662        foreach my $Kind (sort keys(%{$CompatProblems{$Method}}))
2663        {
2664            if(not defined $CompatRules{"Binary"}{$Kind} and not defined $CompatRules{"Source"}{$Kind})
2665            { # unknown rule
2666                if(not $UnknownRules{$Level}{$Kind})
2667                { # only one warning
2668                    printMsg("WARNING", "unknown rule \"$Kind\" (\"$Level\")");
2669                    $UnknownRules{$Level}{$Kind} = 1;
2670                }
2671            }
2672        }
2673    }
2674   
2675    foreach my $Method (sort keys(%CompatProblems))
2676    {
2677        foreach my $Kind (sort keys(%{$CompatProblems{$Method}}))
2678        {
2679            if($CompatRules{$Level}{$Kind}{"Kind"} eq "Methods")
2680            {
2681                my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
2682                foreach my $Loc (sort keys(%{$CompatProblems{$Method}{$Kind}}))
2683                {
2684                    if($Kind eq "Added_Method")
2685                    {
2686                        if($Level eq "Source")
2687                        {
2688                            if($ChangedReturnFromVoid{$Method}) {
2689                                next;
2690                            }
2691                        }
2692                        $Added+=1;
2693                    }
2694                    elsif($Kind eq "Removed_Method")
2695                    {
2696                        if($Level eq "Source")
2697                        {
2698                            if($ChangedReturnFromVoid{$Method}) {
2699                                next;
2700                            }
2701                        }
2702                        $Removed+=1;
2703                        $TotalAffected{$Level}{$Method} = $Severity;
2704                    }
2705                    else
2706                    {
2707                        if($Severity eq "Safe") {
2708                            $M_Other += 1;
2709                        }
2710                        elsif($Severity eq "High") {
2711                            $M_Problems_High+=1;
2712                        }
2713                        elsif($Severity eq "Medium") {
2714                            $M_Problems_Medium+=1;
2715                        }
2716                        elsif($Severity eq "Low") {
2717                            $M_Problems_Low+=1;
2718                        }
2719                        if(($Severity ne "Low" or $In::Opt{"StrictCompat"})
2720                        and $Severity ne "Safe") {
2721                            $TotalAffected{$Level}{$Method} = $Severity;
2722                        }
2723                    }
2724                }
2725            }
2726        }
2727    }
2728   
2729    my %MethodTypeIndex = ();
2730   
2731    foreach my $Method (sort keys(%CompatProblems))
2732    {
2733        foreach my $Kind (sort keys(%{$CompatProblems{$Method}}))
2734        {
2735            if($CompatRules{$Level}{$Kind}{"Kind"} eq "Types")
2736            {
2737                my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
2738                if(($Severity ne "Low" or $In::Opt{"StrictCompat"})
2739                and $Severity ne "Safe")
2740                {
2741                    if(my $Sev = $TotalAffected{$Level}{$Method})
2742                    {
2743                        if($Severity_Val{$Severity}>$Severity_Val{$Sev}) {
2744                            $TotalAffected{$Level}{$Method} = $Severity;
2745                        }
2746                    }
2747                    else {
2748                        $TotalAffected{$Level}{$Method} = $Severity;
2749                    }
2750                }
2751               
2752                my $MK = $CompatProblems{$Method}{$Kind};
2753                my (@Locs1, @Locs2) = ();
2754                foreach my $Loc (sort {length($a)<=>length($b)} sort keys(%{$MK}))
2755                {
2756                    if(index($Loc, "retval")==0 or index($Loc, "this")==0) {
2757                        push(@Locs2, $Loc);
2758                    }
2759                    else {
2760                        push(@Locs1, $Loc);
2761                    }
2762                }
2763                foreach my $Loc (@Locs1, @Locs2)
2764                {
2765                    my $Type = $MK->{$Loc}{"Type_Name"};
2766                    my $Target = $MK->{$Loc}{"Target"};
2767                   
2768                    if(defined $MethodTypeIndex{$Method}{$Type}{$Kind}{$Target})
2769                    { # one location for one type and target
2770                        next;
2771                    }
2772                    $MethodTypeIndex{$Method}{$Type}{$Kind}{$Target} = 1;
2773                   
2774                    $TypeChanges{$Level}{$Type}{$Kind}{$Loc} = $MK->{$Loc};
2775                    $TypeProblemsIndex{$Level}{$Type}{$Kind}{$Loc}{$Method} = 1;
2776                }
2777            }
2778        }
2779    }
2780   
2781    %MethodTypeIndex = (); # clear memory
2782   
2783    $T_Problems_High = getTypeProblemsCount("High", $Level);
2784    $T_Problems_Medium = getTypeProblemsCount("Medium", $Level);
2785    $T_Problems_Low = getTypeProblemsCount("Low", $Level);
2786    $T_Other = getTypeProblemsCount("Safe", $Level);
2787   
2788    my $SCount = keys(%CheckedMethods)-$Added;
2789    if($SCount)
2790    {
2791        my %Weight = (
2792            "High" => 100,
2793            "Medium" => 50,
2794            "Low" => 25
2795        );
2796        foreach (keys(%{$TotalAffected{$Level}})) {
2797            $RESULT{$Level}{"Affected"}+=$Weight{$TotalAffected{$Level}{$_}};
2798        }
2799        $RESULT{$Level}{"Affected"} = $RESULT{$Level}{"Affected"}/$SCount;
2800    }
2801    else {
2802        $RESULT{$Level}{"Affected"} = 0;
2803    }
2804    $RESULT{$Level}{"Affected"} = showNum($RESULT{$Level}{"Affected"});
2805    if($RESULT{$Level}{"Affected"}>=100) {
2806        $RESULT{$Level}{"Affected"} = 100;
2807    }
2808   
2809    my ($TestInfo, $TestResults, $Problem_Summary) = ();
2810   
2811    # test info
2812    $TestInfo .= "<h2>Test Info</h2><hr/>\n";
2813    $TestInfo .= "<table class='summary'>\n";
2814    $TestInfo .= "<tr><th>Library Name</th><td>".$In::Opt{"TargetTitle"}."</td></tr>\n";
2815    $TestInfo .= "<tr><th>Version #1</th><td>".$In::Desc{1}{"Version"}."</td></tr>\n";
2816    $TestInfo .= "<tr><th>Version #2</th><td>".$In::Desc{2}{"Version"}."</td></tr>\n";
2817   
2818    if($In::Opt{"JoinReport"})
2819    {
2820        if($Level eq "Binary") {
2821            $TestInfo .= "<tr><th>Subject</th><td width='150px'>Binary Compatibility</td></tr>\n"; # Run-time
2822        }
2823        if($Level eq "Source") {
2824            $TestInfo .= "<tr><th>Subject</th><td width='150px'>Source Compatibility</td></tr>\n"; # Build-time
2825        }
2826    }
2827    $TestInfo .= "</table>\n";
2828   
2829    # test results
2830    $TestResults .= "<h2>Test Results</h2><hr/>\n";
2831    $TestResults .= "<table class='summary'>\n";
2832   
2833    my $Checked_Archives_Link = "0";
2834    $Checked_Archives_Link = "<a href='#Checked_Archives' style='color:Blue;'>".keys(%{$LibArchives{1}})."</a>" if(keys(%{$LibArchives{1}})>0);
2835   
2836    $TestResults .= "<tr><th>Total Java Modules</th><td>$Checked_Archives_Link</td></tr>\n";
2837    $TestResults .= "<tr><th>Total Methods / Classes</th><td>".keys(%CheckedMethods)." / ".keys(%CheckedTypes)."</td></tr>\n";
2838   
2839    $RESULT{$Level}{"Problems"} += $Removed+$M_Problems_High+$T_Problems_High+$T_Problems_Medium+$M_Problems_Medium;
2840    if($In::Opt{"StrictCompat"}) {
2841        $RESULT{$Level}{"Problems"}+=$T_Problems_Low+$M_Problems_Low;
2842    }
2843    else {
2844        $RESULT{$Level}{"Warnings"}+=$T_Problems_Low+$M_Problems_Low;
2845    }
2846   
2847    my $META_DATA = "kind:".lc($Level).";";
2848    $META_DATA .= $RESULT{$Level}{"Problems"}?"verdict:incompatible;":"verdict:compatible;";
2849    $TestResults .= "<tr><th>Compatibility</th>\n";
2850   
2851    my $BC_Rate = showNum(100 - $RESULT{$Level}{"Affected"});
2852   
2853    if($RESULT{$Level}{"Problems"})
2854    {
2855        my $Cl = "incompatible";
2856        if($BC_Rate>=90) {
2857            $Cl = "warning";
2858        }
2859        elsif($BC_Rate>=80) {
2860            $Cl = "almost_compatible";
2861        }
2862       
2863        $TestResults .= "<td class=\'$Cl\'>".$BC_Rate."%</td>\n";
2864    }
2865    else
2866    {
2867        $TestResults .= "<td class=\'compatible\'>100%</td>\n";
2868    }
2869   
2870    $TestResults .= "</tr>\n";
2871    $TestResults .= "</table>\n";
2872   
2873    $META_DATA .= "affected:".$RESULT{$Level}{"Affected"}.";";# in percents
2874   
2875    # Problem Summary
2876    $Problem_Summary .= "<h2>Problem Summary</h2><hr/>\n";
2877    $Problem_Summary .= "<table class='summary'>\n";
2878    $Problem_Summary .= "<tr><th></th><th style='text-align:center;'>Severity</th><th style='text-align:center;'>Count</th></tr>\n";
2879   
2880    my $Added_Link = "0";
2881    if($Added>0)
2882    {
2883        if($In::Opt{"ShortMode"}) {
2884            $Added_Link = $Added;
2885        }
2886        else
2887        {
2888            if($In::Opt{"JoinReport"}) {
2889                $Added_Link = "<a href='#".$Level."_Added' style='color:Blue;'>$Added</a>";
2890            }
2891            else {
2892                $Added_Link = "<a href='#Added' style='color:Blue;'>$Added</a>";
2893            }
2894        }
2895    }
2896    $META_DATA .= "added:$Added;";
2897    $Problem_Summary .= "<tr><th>Added Methods</th><td>-</td><td".getStyle("M", "Added", $Added).">$Added_Link</td></tr>\n";
2898   
2899    my $Removed_Link = "0";
2900    if($Removed>0)
2901    {
2902        if($In::Opt{"ShortMode"}) {
2903            $Removed_Link = $Removed;
2904        }
2905        else
2906        {
2907            if($In::Opt{"JoinReport"}) {
2908                $Removed_Link = "<a href='#".$Level."_Removed' style='color:Blue;'>$Removed</a>"
2909            }
2910            else {
2911                $Removed_Link = "<a href='#Removed' style='color:Blue;'>$Removed</a>"
2912            }
2913        }
2914    }
2915    $META_DATA .= "removed:$Removed;";
2916    $Problem_Summary .= "<tr><th>Removed Methods</th>";
2917    $Problem_Summary .= "<td>High</td><td".getStyle("M", "Removed", $Removed).">$Removed_Link</td></tr>\n";
2918   
2919    my $TH_Link = "0";
2920    $TH_Link = "<a href='#".getAnchor("Type", $Level, "High")."' style='color:Blue;'>$T_Problems_High</a>" if($T_Problems_High>0);
2921    $META_DATA .= "type_problems_high:$T_Problems_High;";
2922    $Problem_Summary .= "<tr><th rowspan='3'>Problems with<br/>Data Types</th>";
2923    $Problem_Summary .= "<td>High</td><td".getStyle("T", "High", $T_Problems_High).">$TH_Link</td></tr>\n";
2924   
2925    my $TM_Link = "0";
2926    $TM_Link = "<a href='#".getAnchor("Type", $Level, "Medium")."' style='color:Blue;'>$T_Problems_Medium</a>" if($T_Problems_Medium>0);
2927    $META_DATA .= "type_problems_medium:$T_Problems_Medium;";
2928    $Problem_Summary .= "<tr><td>Medium</td><td".getStyle("T", "Medium", $T_Problems_Medium).">$TM_Link</td></tr>\n";
2929   
2930    my $TL_Link = "0";
2931    $TL_Link = "<a href='#".getAnchor("Type", $Level, "Low")."' style='color:Blue;'>$T_Problems_Low</a>" if($T_Problems_Low>0);
2932    $META_DATA .= "type_problems_low:$T_Problems_Low;";
2933    $Problem_Summary .= "<tr><td>Low</td><td".getStyle("T", "Low", $T_Problems_Low).">$TL_Link</td></tr>\n";
2934   
2935    my $MH_Link = "0";
2936    $MH_Link = "<a href='#".getAnchor("Method", $Level, "High")."' style='color:Blue;'>$M_Problems_High</a>" if($M_Problems_High>0);
2937    $META_DATA .= "method_problems_high:$M_Problems_High;";
2938    $Problem_Summary .= "<tr><th rowspan='3'>Problems with<br/>Methods</th>";
2939    $Problem_Summary .= "<td>High</td><td".getStyle("M", "High", $M_Problems_High).">$MH_Link</td></tr>\n";
2940   
2941    my $MM_Link = "0";
2942    $MM_Link = "<a href='#".getAnchor("Method", $Level, "Medium")."' style='color:Blue;'>$M_Problems_Medium</a>" if($M_Problems_Medium>0);
2943    $META_DATA .= "method_problems_medium:$M_Problems_Medium;";
2944    $Problem_Summary .= "<tr><td>Medium</td><td".getStyle("M", "Medium", $M_Problems_Medium).">$MM_Link</td></tr>\n";
2945   
2946    my $ML_Link = "0";
2947    $ML_Link = "<a href='#".getAnchor("Method", $Level, "Low")."' style='color:Blue;'>$M_Problems_Low</a>" if($M_Problems_Low>0);
2948    $META_DATA .= "method_problems_low:$M_Problems_Low;";
2949    $Problem_Summary .= "<tr><td>Low</td><td".getStyle("M", "Low", $M_Problems_Low).">$ML_Link</td></tr>\n";
2950   
2951    # Safe Changes
2952    if($T_Other)
2953    {
2954        my $TS_Link = "<a href='#".getAnchor("Type", $Level, "Safe")."' style='color:Blue;'>$T_Other</a>";
2955        $Problem_Summary .= "<tr><th>Other Changes<br/>in Data Types</th><td>-</td><td".getStyle("T", "Safe", $T_Other).">$TS_Link</td></tr>\n";
2956    }
2957   
2958    if($M_Other)
2959    {
2960        my $MS_Link = "<a href='#".getAnchor("Method", $Level, "Safe")."' style='color:Blue;'>$M_Other</a>";
2961        $Problem_Summary .= "<tr><th>Other Changes<br/>in Methods</th><td>-</td><td".getStyle("M", "Safe", $M_Other).">$MS_Link</td></tr>\n";
2962    }
2963    $META_DATA .= "checked_methods:".keys(%CheckedMethods).";";
2964    $META_DATA .= "checked_types:".keys(%CheckedTypes).";";
2965    $META_DATA .= "tool_version:$TOOL_VERSION";
2966    $Problem_Summary .= "</table>\n";
2967   
2968    my $AnyChanged = ($Added or $Removed or $M_Problems_High or $M_Problems_Medium or $M_Problems_Low or
2969    $T_Problems_High or $T_Problems_Medium or $T_Problems_Low or $M_Other or $T_Other);
2970   
2971    return ($TestInfo.$TestResults.$Problem_Summary, $META_DATA, $AnyChanged);
2972}
2973
2974sub getStyle($$$)
2975{
2976    my ($Subj, $Act, $Num) = @_;
2977    my %Style = (
2978        "Added"=>"new",
2979        "Removed"=>"failed",
2980        "Safe"=>"passed",
2981        "Low"=>"warning",
2982        "Medium"=>"failed",
2983        "High"=>"failed"
2984    );
2985   
2986    if($Num>0) {
2987        return " class='".$Style{$Act}."'";
2988    }
2989   
2990    return "";
2991}
2992
2993sub getAnchor($$$)
2994{
2995    my ($Kind, $Level, $Severity) = @_;
2996    if($In::Opt{"JoinReport"})
2997    {
2998        if($Severity eq "Safe") {
2999            return "Other_".$Level."_Changes_In_".$Kind."s";
3000        }
3001        else {
3002            return $Kind."_".$Level."_Problems_".$Severity;
3003        }
3004    }
3005    else
3006    {
3007        if($Severity eq "Safe") {
3008            return "Other_Changes_In_".$Kind."s";
3009        }
3010        else {
3011            return $Kind."_Problems_".$Severity;
3012        }
3013    }
3014}
3015
3016sub getReportAdded($)
3017{
3018    if($In::Opt{"ShortMode"}) {
3019        return "";
3020    }
3021   
3022    my $Level = $_[0];
3023    my ($ADDED_METHODS, %MethodAddedInArchiveClass);
3024    foreach my $Method (sort keys(%CompatProblems))
3025    {
3026        foreach my $Kind (sort keys(%{$CompatProblems{$Method}}))
3027        {
3028            if($Kind eq "Added_Method")
3029            {
3030                my $ArchiveName = $MethodInfo{2}{$Method}{"Archive"};
3031                my $ClassName = getShortName($MethodInfo{2}{$Method}{"Class"}, 2);
3032                if($Level eq "Source")
3033                {
3034                    if($ChangedReturnFromVoid{$Method}) {
3035                        next;
3036                    }
3037                }
3038                $MethodAddedInArchiveClass{$ArchiveName}{$ClassName}{$Method} = 1;
3039            }
3040        }
3041    }
3042    my $Added_Number = 0;
3043    foreach my $ArchiveName (sort {lc($a) cmp lc($b)} keys(%MethodAddedInArchiveClass))
3044    {
3045        foreach my $ClassName (sort {lc($a) cmp lc($b)} keys(%{$MethodAddedInArchiveClass{$ArchiveName}}))
3046        {
3047            my %NameSpace_Method = ();
3048            foreach my $Method (keys(%{$MethodAddedInArchiveClass{$ArchiveName}{$ClassName}})) {
3049                $NameSpace_Method{$MethodInfo{2}{$Method}{"Package"}}{$Method} = 1;
3050            }
3051           
3052            my $ShowClass = $ClassName;
3053            $ShowClass=~s/<.*>//g;
3054           
3055            foreach my $NameSpace (sort keys(%NameSpace_Method))
3056            {
3057                $ADDED_METHODS .= "<span class='jar'>$ArchiveName</span>, <span class='cname'>".specChars($ShowClass).".class</span><br/>\n";
3058               
3059                if($NameSpace) {
3060                    $ADDED_METHODS .= "<span class='pkg_t'>package</span> <span class='pkg'>$NameSpace</span><br/>\n";
3061                }
3062               
3063                if($In::Opt{"Compact"}) {
3064                    $ADDED_METHODS .= "<div class='symbols'>";
3065                }
3066               
3067                my @SortedMethods = sort {lc($MethodInfo{2}{$a}{"Signature"}) cmp lc($MethodInfo{2}{$b}{"Signature"})} sort keys(%{$NameSpace_Method{$NameSpace}});
3068                foreach my $Method (@SortedMethods)
3069                {
3070                    $Added_Number += 1;
3071                   
3072                    my $Signature = undef;
3073                   
3074                    if($In::Opt{"Compact"}) {
3075                        $Signature = getSignature($Method, 2, "Full|HTML|Simple");
3076                    }
3077                    else {
3078                        $Signature = highLight_ItalicColor($Method, 2);
3079                    }
3080                   
3081                    if($NameSpace) {
3082                        $Signature=~s/(\W|\A)\Q$NameSpace\E\.(\w)/$1$2/g;
3083                    }
3084                   
3085                    if($In::Opt{"Compact"}) {
3086                        $ADDED_METHODS .= "&nbsp;".$Signature."<br/>\n";
3087                    }
3088                    else {
3089                        $ADDED_METHODS .= insertIDs($ContentSpanStart.$Signature.$ContentSpanEnd."<br/>\n".$ContentDivStart."<span class='mngl'>".specChars($Method)."</span><br/><br/>".$ContentDivEnd."\n");
3090                    }
3091                }
3092               
3093                if($In::Opt{"Compact"}) {
3094                    $ADDED_METHODS .= "</div>";
3095                }
3096               
3097                $ADDED_METHODS .= "<br/>\n";
3098            }
3099           
3100        }
3101    }
3102    if($ADDED_METHODS)
3103    {
3104        my $Anchor = "<a name='Added'></a>";
3105        if($In::Opt{"JoinReport"}) {
3106            $Anchor = "<a name='".$Level."_Added'></a>";
3107        }
3108        if($In::Opt{"OldStyle"}) {
3109            $ADDED_METHODS = "<h2>Added Methods ($Added_Number)</h2><hr/>\n".$ADDED_METHODS;
3110        }
3111        else {
3112            $ADDED_METHODS = "<h2>Added Methods <span".getStyle("M", "Added", $Added_Number).">&nbsp;$Added_Number&nbsp;</span></h2><hr/>\n".$ADDED_METHODS;
3113        }
3114        $ADDED_METHODS = $Anchor.$ADDED_METHODS.$TOP_REF."<br/>\n";
3115    }
3116    return $ADDED_METHODS;
3117}
3118
3119sub getReportRemoved($)
3120{
3121    if($In::Opt{"ShortMode"}) {
3122        return "";
3123    }
3124   
3125    my $Level = $_[0];
3126    my ($REMOVED_METHODS, %MethodRemovedFromArchiveClass);
3127    foreach my $Method (sort keys(%CompatProblems))
3128    {
3129        foreach my $Kind (sort keys(%{$CompatProblems{$Method}}))
3130        {
3131            if($Kind eq "Removed_Method")
3132            {
3133                if($Level eq "Source")
3134                {
3135                    if($ChangedReturnFromVoid{$Method}) {
3136                        next;
3137                    }
3138                }
3139                my $ArchiveName = $MethodInfo{1}{$Method}{"Archive"};
3140                my $ClassName = getShortName($MethodInfo{1}{$Method}{"Class"}, 1);
3141                $MethodRemovedFromArchiveClass{$ArchiveName}{$ClassName}{$Method} = 1;
3142            }
3143        }
3144    }
3145    my $Removed_Number = 0;
3146    foreach my $ArchiveName (sort {lc($a) cmp lc($b)} keys(%MethodRemovedFromArchiveClass))
3147    {
3148        foreach my $ClassName (sort {lc($a) cmp lc($b)} keys(%{$MethodRemovedFromArchiveClass{$ArchiveName}}))
3149        {
3150            my %NameSpace_Method = ();
3151            foreach my $Method (keys(%{$MethodRemovedFromArchiveClass{$ArchiveName}{$ClassName}}))
3152            {
3153                $NameSpace_Method{$MethodInfo{1}{$Method}{"Package"}}{$Method} = 1;
3154            }
3155           
3156            my $ShowClass = $ClassName;
3157            $ShowClass=~s/<.*>//g;
3158           
3159            foreach my $NameSpace (sort keys(%NameSpace_Method))
3160            {
3161                $REMOVED_METHODS .= "<span class='jar'>$ArchiveName</span>, <span class='cname'>".specChars($ShowClass).".class</span><br/>\n";
3162               
3163                if($NameSpace) {
3164                    $REMOVED_METHODS .= "<span class='pkg_t'>package</span> <span class='pkg'>$NameSpace</span><br/>\n";
3165                }
3166               
3167                if($In::Opt{"Compact"}) {
3168                    $REMOVED_METHODS .= "<div class='symbols'>";
3169                }
3170               
3171                my @SortedMethods = sort {lc($MethodInfo{1}{$a}{"Signature"}) cmp lc($MethodInfo{1}{$b}{"Signature"})} sort keys(%{$NameSpace_Method{$NameSpace}});
3172                foreach my $Method (@SortedMethods)
3173                {
3174                    $Removed_Number += 1;
3175                   
3176                    my $Signature = undef;
3177                   
3178                    if($In::Opt{"Compact"}) {
3179                        $Signature = getSignature($Method, 1, "Full|HTML|Simple");
3180                    }
3181                    else {
3182                        $Signature = highLight_ItalicColor($Method, 1);
3183                    }
3184                   
3185                    if($NameSpace) {
3186                        $Signature=~s/(\W|\A)\Q$NameSpace\E\.(\w)/$1$2/g;
3187                    }
3188                   
3189                    if($In::Opt{"Compact"}) {
3190                        $REMOVED_METHODS .= "&nbsp;".$Signature."<br/>\n";
3191                    }
3192                    else {
3193                        $REMOVED_METHODS .= insertIDs($ContentSpanStart.$Signature.$ContentSpanEnd."<br/>\n".$ContentDivStart."<span class='mngl'>".specChars($Method)."</span><br/><br/>".$ContentDivEnd."\n");
3194                    }
3195                }
3196               
3197                if($In::Opt{"Compact"}) {
3198                    $REMOVED_METHODS .= "</div>";
3199                }
3200               
3201                $REMOVED_METHODS .= "<br/>\n";
3202            }
3203        }
3204    }
3205    if($REMOVED_METHODS)
3206    {
3207        my $Anchor = "<a name='Removed'></a><a name='Withdrawn'></a>";
3208        if($In::Opt{"JoinReport"}) {
3209            $Anchor = "<a name='".$Level."_Removed'></a><a name='".$Level."_Withdrawn'></a>";
3210        }
3211        if($In::Opt{"OldStyle"}) {
3212            $REMOVED_METHODS = "<h2>Removed Methods ($Removed_Number)</h2><hr/>\n".$REMOVED_METHODS;
3213        }
3214        else {
3215            $REMOVED_METHODS = "<h2>Removed Methods <span".getStyle("M", "Removed", $Removed_Number).">&nbsp;$Removed_Number&nbsp;</span></h2><hr/>\n".$REMOVED_METHODS;
3216        }
3217        $REMOVED_METHODS = $Anchor.$REMOVED_METHODS.$TOP_REF."<br/>\n";
3218    }
3219    return $REMOVED_METHODS;
3220}
3221
3222sub readRules($)
3223{
3224    my $Kind = $_[0];
3225    if(not -f $RULES_PATH{$Kind}) {
3226        exitStatus("Module_Error", "can't access \'".$RULES_PATH{$Kind}."\'");
3227    }
3228    my $Content = readFile($RULES_PATH{$Kind});
3229    while(my $Rule = parseTag(\$Content, "rule"))
3230    {
3231        my $RId = parseTag(\$Rule, "id");
3232        my @Properties = ("Severity", "Change", "Effect", "Overcome", "Kind");
3233        foreach my $Prop (@Properties) {
3234            if(my $Value = parseTag(\$Rule, lc($Prop)))
3235            {
3236                $Value=~s/\n[ ]*//;
3237                $CompatRules{$Kind}{$RId}{$Prop} = $Value;
3238            }
3239        }
3240        if($CompatRules{$Kind}{$RId}{"Kind"}=~/\A(Methods|Parameters)\Z/) {
3241            $CompatRules{$Kind}{$RId}{"Kind"} = "Methods";
3242        }
3243        else { # Types, Fields
3244            $CompatRules{$Kind}{$RId}{"Kind"} = "Types";
3245        }
3246    }
3247}
3248
3249sub addMarkup($)
3250{
3251    my $Content = $_[0];
3252   
3253    # auto-markup
3254    $Content=~s/\n[ ]*//; # spaces
3255    $Content=~s!([2-9]\))!<br/>$1!g; # 1), 2), ...
3256    if($Content=~/\ANOTE:/)
3257    { # notes
3258        $Content=~s!(NOTE):!<b>$1</b>:!g;
3259    }
3260    else {
3261        $Content=~s!(NOTE):!<br/><br/><b>$1</b>:!g;
3262    }
3263   
3264    my @Keywords = (
3265        "static",
3266        "abstract",
3267        "default",
3268        "final",
3269        "synchronized"
3270    );
3271   
3272    my $MKeys = join("|", @Keywords);
3273    foreach (@Keywords) {
3274        $MKeys .= "|non-".$_;
3275    }
3276   
3277    $Content=~s!(became\s*)($MKeys)([^\w-]|\Z)!$1<b>$2</b>$3!ig; # intrinsic types, modifiers
3278   
3279    # Markdown
3280    $Content=~s!\*\*([\w\-\@]+)\*\*!<b>$1</b>!ig;
3281    $Content=~s!\*([\w\-]+)\*!<i>$1</i>!ig;
3282   
3283    return $Content;
3284}
3285
3286sub applyMacroses($$$$$$)
3287{
3288    my ($Level, $Subj, $Kind, $Content, $Problem, $AddAttr) = @_;
3289   
3290    $Content = addMarkup($Content);
3291   
3292    # macros
3293    foreach my $Attr (sort {$b cmp $a} (keys(%{$Problem}), keys(%{$AddAttr})))
3294    {
3295        my $Macro = "\@".lc($Attr);
3296        my $Value = undef;
3297       
3298        if(defined $Problem->{$Attr}) {
3299            $Value = $Problem->{$Attr};
3300        }
3301        else {
3302            $Value = $AddAttr->{$Attr};
3303        }
3304       
3305        if(not defined $Value
3306        or $Value eq "") {
3307            next;
3308        }
3309       
3310        if(index($Content, $Macro)==-1) {
3311            next;
3312        }
3313       
3314        if($Attr eq "Param_Pos") {
3315            $Value = showPos($Value);
3316        }
3317       
3318        if($Attr eq "Invoked") {
3319            $Value = blackName(specChars($Value));
3320        }
3321        elsif($Value=~/\s/) {
3322            $Value = "<span class='value'>".specChars($Value)."</span>";
3323        }
3324        else
3325        {
3326            my $Fmt = "Class|HTML|Desc";
3327           
3328            if($Attr ne "Invoked_By")
3329            {
3330                if($Attr eq "Method_Short"
3331                or $Kind!~/Overridden|Moved_Up/) {
3332                    $Fmt = "HTML|Desc";
3333                }
3334            }
3335           
3336            if($Subj eq "Change") {
3337                $Fmt .= "|Return";
3338            }
3339           
3340            if(defined $MethodInfo{1}{$Value}
3341            and defined $MethodInfo{1}{$Value}{"ShortName"}) {
3342                $Value = blackName(getSignature($Value, 1, $Fmt));
3343            }
3344            elsif(defined $MethodInfo{2}{$Value}
3345            and defined $MethodInfo{2}{$Value}{"ShortName"}) {
3346                $Value = blackName(getSignature($Value, 2, $Fmt));
3347            }
3348            else
3349            {
3350                $Value = specChars($Value);
3351                if($Attr ne "Type_Type") {
3352                    $Value = "<b>".$Value."</b>";
3353                }
3354            }
3355        }
3356        $Content=~s/\Q$Macro\E/$Value/g;
3357    }
3358   
3359    if($Content=~/(\A|[^\@\w])(\@\w+)/)
3360    {
3361        if(not $IncompleteRules{$Level}{$Kind})
3362        { # only one warning
3363            printMsg("WARNING", "incomplete $2 in the rule \"$Kind\" (\"$Level\")");
3364            $IncompleteRules{$Level}{$Kind} = 1;
3365        }
3366    }
3367   
3368    return $Content;
3369}
3370
3371sub getReportMethodProblems($$)
3372{
3373    my ($TargetSeverity, $Level) = @_;
3374    my $METHOD_PROBLEMS = "";
3375    my (%ReportMap, %MethodChanges) = ();
3376   
3377    foreach my $Method (sort keys(%CompatProblems))
3378    {
3379        my $ArchiveName = $MethodInfo{1}{$Method}{"Archive"};
3380        my $ClassName = getShortName($MethodInfo{1}{$Method}{"Class"}, 1);
3381       
3382        foreach my $Kind (sort keys(%{$CompatProblems{$Method}}))
3383        {
3384            if($CompatRules{$Level}{$Kind}{"Kind"} eq "Methods")
3385            {
3386                if($Kind eq "Added_Method"
3387                or $Kind eq "Removed_Method") {
3388                    next;
3389                }
3390               
3391                if(my $Severity = $CompatRules{$Level}{$Kind}{"Severity"})
3392                {
3393                    if($Severity ne $TargetSeverity) {
3394                        next;
3395                    }
3396                   
3397                    $MethodChanges{$Method}{$Kind} = $CompatProblems{$Method}{$Kind};
3398                    $ReportMap{$ArchiveName}{$ClassName}{$Method} = 1;
3399                }
3400            }
3401        }
3402    }
3403    my $ProblemsNum = 0;
3404    foreach my $ArchiveName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
3405    {
3406        foreach my $ClassName (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$ArchiveName}}))
3407        {
3408            my %NameSpace_Method = ();
3409            foreach my $Method (keys(%{$ReportMap{$ArchiveName}{$ClassName}})) {
3410                $NameSpace_Method{$MethodInfo{1}{$Method}{"Package"}}{$Method} = 1;
3411            }
3412           
3413            my $ShowClass = $ClassName;
3414            $ShowClass=~s/<.*>//g;
3415           
3416            foreach my $NameSpace (sort keys(%NameSpace_Method))
3417            {
3418                $METHOD_PROBLEMS .= "<span class='jar'>$ArchiveName</span>, <span class='cname'>".specChars($ShowClass).".class</span><br/>\n";
3419                if($NameSpace) {
3420                    $METHOD_PROBLEMS .= "<span class='pkg_t'>package</span> <span class='pkg'>$NameSpace</span><br/>\n";
3421                }
3422               
3423                my @SortedMethods = sort {lc($MethodInfo{1}{$a}{"Signature"}) cmp lc($MethodInfo{1}{$b}{"Signature"})} sort keys(%{$NameSpace_Method{$NameSpace}});
3424                foreach my $Method (@SortedMethods)
3425                {
3426                    my %AddAttr = ();
3427                   
3428                    $AddAttr{"Method_Short"} = $Method;
3429                    $AddAttr{"Class"} = getTypeName($MethodInfo{1}{$Method}{"Class"}, 1);
3430                   
3431                    my $METHOD_REPORT = "";
3432                    my $ProblemNum = 1;
3433                    foreach my $Kind (sort keys(%{$MethodChanges{$Method}}))
3434                    {
3435                        foreach my $Loc (sort keys(%{$MethodChanges{$Method}{$Kind}}))
3436                        {
3437                            my $ProblemAttr = $MethodChanges{$Method}{$Kind}{$Loc};
3438                           
3439                            if(my $Change = applyMacroses($Level, "Change", $Kind, $CompatRules{$Level}{$Kind}{"Change"}, $ProblemAttr, \%AddAttr))
3440                            {
3441                                my $Effect = applyMacroses($Level, "Effect", $Kind, $CompatRules{$Level}{$Kind}{"Effect"}, $ProblemAttr, \%AddAttr);
3442                                $METHOD_REPORT .= "<tr>\n<th>$ProblemNum</th>\n<td>".$Change."</td>\n<td>".$Effect."</td>\n</tr>\n";
3443                                $ProblemNum += 1;
3444                                $ProblemsNum += 1;
3445                            }
3446                        }
3447                    }
3448                    $ProblemNum -= 1;
3449                    if($METHOD_REPORT)
3450                    {
3451                        my $ShowMethod = highLight_ItalicColor($Method, 1);
3452                        if($NameSpace)
3453                        {
3454                            $METHOD_REPORT = cutNs($METHOD_REPORT, $NameSpace);
3455                            $ShowMethod = cutNs($ShowMethod, $NameSpace);
3456                        }
3457                       
3458                        $METHOD_PROBLEMS .= $ContentSpanStart."<span class='ext'>[+]</span> ".$ShowMethod;
3459                        if($In::Opt{"OldStyle"}) {
3460                            $METHOD_PROBLEMS .= " ($ProblemNum)";
3461                        }
3462                        else {
3463                            $METHOD_PROBLEMS .= " <span".getStyle("M", $TargetSeverity, $ProblemNum).">&nbsp;$ProblemNum&nbsp;</span>";
3464                        }
3465                        $METHOD_PROBLEMS .= $ContentSpanEnd."<br/>\n";
3466                        $METHOD_PROBLEMS .= $ContentDivStart;
3467                       
3468                        if(not $In::Opt{"Compact"}) {
3469                            $METHOD_PROBLEMS .= "<span class='mngl pleft'>".specChars($Method)."</span><br/>\n";
3470                        }
3471                       
3472                        $METHOD_PROBLEMS .= "<table class='ptable'><tr><th width='2%'></th><th width='47%'>Change</th><th>Effect</th></tr>$METHOD_REPORT</table><br/>$ContentDivEnd\n";
3473                       
3474                    }
3475                }
3476               
3477                $METHOD_PROBLEMS .= "<br/>";
3478            }
3479        }
3480    }
3481    if($METHOD_PROBLEMS)
3482    {
3483        $METHOD_PROBLEMS = insertIDs($METHOD_PROBLEMS);
3484       
3485        my $Title = "Problems with Methods, $TargetSeverity Severity";
3486        if($TargetSeverity eq "Safe")
3487        { # Safe Changes
3488            $Title = "Other Changes in Methods";
3489        }
3490        if($In::Opt{"OldStyle"}) {
3491            $METHOD_PROBLEMS = "<h2>$Title ($ProblemsNum)</h2><hr/>\n".$METHOD_PROBLEMS;
3492        }
3493        else {
3494            $METHOD_PROBLEMS = "<h2>$Title <span".getStyle("M", $TargetSeverity, $ProblemsNum).">&nbsp;$ProblemsNum&nbsp;</span></h2><hr/>\n".$METHOD_PROBLEMS;
3495        }
3496        $METHOD_PROBLEMS = "<a name='".getAnchor("Method", $Level, $TargetSeverity)."'></a>\n".$METHOD_PROBLEMS;
3497        $METHOD_PROBLEMS .= $TOP_REF."<br/>\n";
3498    }
3499    return $METHOD_PROBLEMS;
3500}
3501
3502sub showType($$$)
3503{
3504    my ($Name, $Html, $LVer) = @_;
3505    my $TInfo = $TypeInfo{$LVer}{$TName_Tid{$LVer}{$Name}};
3506    my $TType = $TInfo->{"Type"};
3507   
3508    if($TInfo->{"Annotation"}) {
3509        $TType = '@'.$TType;
3510    }
3511   
3512    if($Html) {
3513        $Name = "<span class='ttype'>".$TType."</span> ".specChars($Name);
3514    }
3515    else {
3516        $Name = $TType." ".$Name;
3517    }
3518    return $Name;
3519}
3520
3521sub getReportTypeProblems($$)
3522{
3523    my ($TargetSeverity, $Level) = @_;
3524    my $TYPE_PROBLEMS = "";
3525   
3526    my %ReportMap = ();
3527    my %TypeChanges_Sev = ();
3528   
3529    foreach my $TypeName (keys(%{$TypeChanges{$Level}}))
3530    {
3531        my $ArchiveName = $TypeInfo{1}{$TName_Tid{1}{$TypeName}}{"Archive"};
3532       
3533        foreach my $Kind (keys(%{$TypeChanges{$Level}{$TypeName}}))
3534        {
3535            if($CompatRules{$Level}{$Kind}{"Severity"} ne $TargetSeverity) {
3536                next;
3537            }
3538           
3539            foreach my $Loc (keys(%{$TypeChanges{$Level}{$TypeName}{$Kind}}))
3540            {
3541                $ReportMap{$ArchiveName}{$TypeName} = 1;
3542                $TypeChanges_Sev{$TypeName}{$Kind}{$Loc} = $TypeChanges{$Level}{$TypeName}{$Kind}{$Loc};
3543            }
3544        }
3545    }
3546   
3547    my $ProblemsNum = 0;
3548    foreach my $ArchiveName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
3549    {
3550        my %NameSpace_Type = ();
3551        foreach my $TypeName (keys(%{$ReportMap{$ArchiveName}})) {
3552            $NameSpace_Type{$TypeInfo{1}{$TName_Tid{1}{$TypeName}}{"Package"}}{$TypeName} = 1;
3553        }
3554        foreach my $NameSpace (sort keys(%NameSpace_Type))
3555        {
3556            $TYPE_PROBLEMS .= "<span class='jar'>$ArchiveName</span><br/>\n";
3557            if($NameSpace) {
3558                $TYPE_PROBLEMS .= "<span class='pkg_t'>package</span> <span class='pkg'>".$NameSpace."</span><br/>\n";
3559            }
3560           
3561            my @SortedTypes = sort {lc(showType($a, 0, 1)) cmp lc(showType($b, 0, 1))} keys(%{$NameSpace_Type{$NameSpace}});
3562            foreach my $TypeName (@SortedTypes)
3563            {
3564                my $TypeId = $TName_Tid{1}{$TypeName};
3565               
3566                my $ProblemNum = 1;
3567                my $TYPE_REPORT = "";
3568                my (%Kinds_Locations, %Kinds_Target) = ();
3569               
3570                foreach my $Kind (sort keys(%{$TypeChanges_Sev{$TypeName}}))
3571                {
3572                    foreach my $Location (sort keys(%{$TypeChanges_Sev{$TypeName}{$Kind}}))
3573                    {
3574                        $Kinds_Locations{$Kind}{$Location} = 1;
3575                       
3576                        my $Target = $TypeChanges_Sev{$TypeName}{$Kind}{$Location}{"Target"};
3577                        if($Kinds_Target{$Kind}{$Target}) {
3578                            next;
3579                        }
3580                        $Kinds_Target{$Kind}{$Target} = 1;
3581                       
3582                        my %AddAttr = ();
3583                       
3584                        if($Kind=~/Method/)
3585                        {
3586                            if(defined $MethodInfo{1}{$Target} and $MethodInfo{1}{$Target}{"Name"})
3587                            {
3588                                $AddAttr{"Method_Short"} = $Target;
3589                                $AddAttr{"Class"} = getTypeName($MethodInfo{1}{$Target}{"Class"}, 1);
3590                            }
3591                            elsif(defined $MethodInfo{2}{$Target} and $MethodInfo{2}{$Target}{"Name"})
3592                            {
3593                                $AddAttr{"Method_Short"} = $Target;
3594                                $AddAttr{"Class"} = getTypeName($MethodInfo{2}{$Target}{"Class"}, 2);
3595                            }
3596                        }
3597                       
3598                        my $ProblemAttr = $TypeChanges_Sev{$TypeName}{$Kind}{$Location};
3599                       
3600                        if(my $Change = applyMacroses($Level, "Change", $Kind, $CompatRules{$Level}{$Kind}{"Change"}, $ProblemAttr, \%AddAttr))
3601                        {
3602                            my $Effect = applyMacroses($Level, "Effect", $Kind, $CompatRules{$Level}{$Kind}{"Effect"}, $ProblemAttr, \%AddAttr);
3603                           
3604                            $TYPE_REPORT .= "<tr>\n<th>$ProblemNum</th>\n<td>".$Change."</td>\n<td>".$Effect."</td>\n</tr>\n";
3605                            $ProblemNum += 1;
3606                            $ProblemsNum += 1;
3607                        }
3608                    }
3609                }
3610                $ProblemNum -= 1;
3611                if($TYPE_REPORT)
3612                {
3613                    my $Affected = "";
3614                    if(not defined $TypeInfo{1}{$TypeId}{"Annotation"}) {
3615                        $Affected = getAffectedMethods($Level, $TypeName, \%Kinds_Locations);
3616                    }
3617                   
3618                    my $ShowType = showType($TypeName, 1, 1);
3619                    if($NameSpace)
3620                    {
3621                        $TYPE_REPORT = cutNs($TYPE_REPORT, $NameSpace);
3622                        $ShowType = cutNs($ShowType, $NameSpace);
3623                        $Affected = cutNs($Affected, $NameSpace);
3624                    }
3625                   
3626                    $TYPE_PROBLEMS .= $ContentSpanStart."<span class='ext'>[+]</span> ".$ShowType;
3627                    if($In::Opt{"OldStyle"}) {
3628                        $TYPE_PROBLEMS .= " ($ProblemNum)";
3629                    }
3630                    else {
3631                        $TYPE_PROBLEMS .= " <span".getStyle("T", $TargetSeverity, $ProblemNum).">&nbsp;$ProblemNum&nbsp;</span>";
3632                    }
3633                    $TYPE_PROBLEMS .= $ContentSpanEnd."<br/>\n";
3634                    $TYPE_PROBLEMS .= $ContentDivStart."<table class='ptable'><tr>";
3635                    $TYPE_PROBLEMS .= "<th width='2%'></th><th width='47%'>Change</th><th>Effect</th>";
3636                    $TYPE_PROBLEMS .= "</tr>$TYPE_REPORT</table>".$Affected."<br/><br/>$ContentDivEnd\n";
3637                }
3638            }
3639           
3640            $TYPE_PROBLEMS .= "<br/>";
3641        }
3642    }
3643    if($TYPE_PROBLEMS)
3644    {
3645        $TYPE_PROBLEMS = insertIDs($TYPE_PROBLEMS);
3646       
3647        my $Title = "Problems with Data Types, $TargetSeverity Severity";
3648        if($TargetSeverity eq "Safe")
3649        { # Safe Changes
3650            $Title = "Other Changes in Data Types";
3651        }
3652        if($In::Opt{"OldStyle"}) {
3653            $TYPE_PROBLEMS = "<h2>$Title ($ProblemsNum)</h2><hr/>\n".$TYPE_PROBLEMS;
3654        }
3655        else {
3656            $TYPE_PROBLEMS = "<h2>$Title <span".getStyle("T", $TargetSeverity, $ProblemsNum).">&nbsp;$ProblemsNum&nbsp;</span></h2><hr/>\n".$TYPE_PROBLEMS;
3657        }
3658        $TYPE_PROBLEMS = "<a name='".getAnchor("Type", $Level, $TargetSeverity)."'></a>\n".$TYPE_PROBLEMS;
3659        $TYPE_PROBLEMS .= $TOP_REF."<br/>\n";
3660    }
3661    return $TYPE_PROBLEMS;
3662}
3663
3664sub cutNs($$)
3665{
3666    my ($N, $Ns) = @_;
3667    $N=~s/(\W|\A)\Q$Ns\E\.(\w)/$1$2/g;
3668    return $N;
3669}
3670
3671sub getAffectedMethods($$$)
3672{
3673    my ($Level, $Target_TypeName, $Kinds_Locations) = @_;
3674   
3675    my $LIMIT = 10;
3676    if(defined $In::Opt{"AffectLimit"}) {
3677        $LIMIT = $In::Opt{"AffectLimit"};
3678    }
3679   
3680    my %SymSel = ();
3681   
3682    foreach my $Kind (sort keys(%{$Kinds_Locations}))
3683    {
3684        my @Locs = sort {(index($a, "retval")!=-1) cmp (index($b, "retval")!=-1)} sort {length($a)<=>length($b)} sort keys(%{$Kinds_Locations->{$Kind}});
3685       
3686        foreach my $Loc (@Locs)
3687        {
3688            foreach my $Method (keys(%{$TypeProblemsIndex{$Level}{$Target_TypeName}{$Kind}{$Loc}}))
3689            {
3690                if($Method eq ".client_method") {
3691                    next;
3692                }
3693               
3694                if(not defined $SymSel{$Method})
3695                {
3696                    $SymSel{$Method}{"Kind"} = $Kind;
3697                    $SymSel{$Method}{"Loc"} = $Loc;
3698                }
3699            }
3700        }
3701    }
3702   
3703    my $Total = keys(%SymSel);
3704   
3705    if(not $Total) {
3706        return "";
3707    }
3708   
3709    my $Affected = "";
3710    my $SNum = 0;
3711   
3712    foreach my $Method (sort {lc($a) cmp lc($b)} keys(%SymSel))
3713    {
3714        my $Kind = $SymSel{$Method}{"Kind"};
3715        my $Loc = $SymSel{$Method}{"Loc"};
3716       
3717        my $Desc = getAffectDesc($Method, $Kind, $Loc, $Level);
3718        my $PName = getParamName($Loc);
3719        my $Pos = getParamPos($PName, $Method, 1);
3720       
3721        $Affected .= "<span class='iname_a'>".getSignature($Method, 1, "HTML|Italic|Param|Class|Target=".$Pos)."</span><br/>";
3722        $Affected .= "<div class='affect'>".$Desc."</div>\n";
3723       
3724        if(++$SNum>=$LIMIT) {
3725            last;
3726        }
3727    }
3728   
3729    if($Total>$LIMIT) {
3730        $Affected .= " <b>...</b>\n<br/>\n"; # and others ...
3731    }
3732   
3733    $Affected = "<div class='affected'>".$Affected."</div>";
3734    if($Affected)
3735    {
3736        my $Per = showNum($Total*100/keys(%CheckedMethods));
3737        $Affected =  $ContentDivStart.$Affected.$ContentDivEnd;
3738        $Affected =  $ContentSpanStart_Affected."[+] affected methods: $Total ($Per\%)".$ContentSpanEnd.$Affected;
3739    }
3740   
3741    return $Affected;
3742}
3743
3744sub getAffectDesc($$$$)
3745{
3746    my ($Method, $Kind, $Location, $Level) = @_;
3747    my %Affect = %{$CompatProblems{$Method}{$Kind}{$Location}};
3748    my $New_Value = $Affect{"New_Value"};
3749    my $Type_Name = $Affect{"Type_Name"};
3750    my @Sentence_Parts = ();
3751   
3752    $Location=~s/\.[^.]+?\Z//;
3753   
3754    my $TypeAttr = getType($MethodInfo{1}{$Method}{"Class"}, 1);
3755    my $Type_Type = $TypeAttr->{"Type"};
3756   
3757    my $ABSTRACT_M = $MethodInfo{1}{$Method}{"Abstract"}?" abstract":"";
3758    my $ABSTRACT_C = $TypeAttr->{"Abstract"}?" abstract":"";
3759    my $METHOD_TYPE = $MethodInfo{1}{$Method}{"Constructor"}?"constructor":"method";
3760   
3761    if($Kind eq "Class_Overridden_Method" or $Kind eq "Class_Method_Moved_Up_Hierarchy") {
3762        return "Method '".getSignature($New_Value, 2, "Class|HTML|Italic")."' will be called instead of this method in a client program.";
3763    }
3764    elsif($CompatRules{$Level}{$Kind}{"Kind"} eq "Types")
3765    {
3766        my %MInfo = %{$MethodInfo{1}{$Method}};
3767       
3768        if($Location eq "this") {
3769            return "This$ABSTRACT_M $METHOD_TYPE is from \'".specChars($Type_Name)."\'$ABSTRACT_C $Type_Type.";
3770        }
3771       
3772        my $TypeID = undef;
3773       
3774        if($Location=~/retval/)
3775        { # return value
3776            if($Location=~/\./) {
3777                push(@Sentence_Parts, "Field \'".specChars($Location)."\' in the return value");
3778            }
3779            else {
3780                push(@Sentence_Parts, "Return value");
3781            }
3782           
3783            $TypeID = $MInfo{"Return"};
3784        }
3785        elsif($Location=~/this/)
3786        { # "this" reference
3787            push(@Sentence_Parts, "Field \'".specChars($Location)."\' in the object");
3788           
3789            $TypeID = $MInfo{"Class"};
3790        }
3791        else
3792        { # parameters
3793            my $PName = getParamName($Location);
3794            my $PPos = getParamPos($PName, $Method, 1);
3795           
3796            if($Location=~/\./) {
3797                push(@Sentence_Parts, "Field \'".specChars($Location)."\' in ".showPos($PPos)." parameter");
3798            }
3799            else {
3800                push(@Sentence_Parts, showPos($PPos)." parameter");
3801            }
3802            if($PName) {
3803                push(@Sentence_Parts, "\'$PName\'");
3804            }
3805           
3806            if(defined $MInfo{"Param"}) {
3807                $TypeID = $MInfo{"Param"}{$PPos}{"Type"};
3808            }
3809        }
3810        push(@Sentence_Parts, " of this$ABSTRACT_M method");
3811       
3812        my $Location_T = $Location;
3813        $Location_T=~s/\A\w+(\.|\Z)//; # location in type
3814       
3815        my $TypeID_Problem = $TypeID;
3816        if($Location_T) {
3817            $TypeID_Problem = getFieldType($Location_T, $TypeID, 1);
3818        }
3819       
3820        if($TypeInfo{1}{$TypeID_Problem}{"Name"} eq $Type_Name) {
3821            push(@Sentence_Parts, "is of type \'".specChars($Type_Name)."\'.");
3822        }
3823        else {
3824            push(@Sentence_Parts, "has base type \'".specChars($Type_Name)."\'.");
3825        }
3826    }
3827    return join(" ", @Sentence_Parts);
3828}
3829
3830sub getParamPos($$$)
3831{
3832    my ($Name, $Method, $LVer) = @_;
3833   
3834    if(defined $MethodInfo{$LVer}{$Method}
3835    and defined $MethodInfo{$LVer}{$Method}{"Param"})
3836    {
3837        my $Info = $MethodInfo{$LVer}{$Method};
3838        foreach (keys(%{$Info->{"Param"}}))
3839        {
3840            if($Info->{"Param"}{$_}{"Name"} eq $Name)
3841            {
3842                return $_;
3843            }
3844        }
3845    }
3846   
3847    return undef;
3848}
3849
3850sub getParamName($)
3851{
3852    my $Loc = $_[0];
3853    $Loc=~s/\..*//g;
3854    return $Loc;
3855}
3856
3857sub getFieldType($$$)
3858{
3859    my ($Location, $TypeId, $LVer) = @_;
3860   
3861    my @Fields = split(/\./, $Location);
3862   
3863    foreach my $Name (@Fields)
3864    {
3865        my $TInfo = getBaseType($TypeId, $LVer);
3866       
3867        foreach my $N (keys(%{$TInfo->{"Fields"}}))
3868        {
3869            if($N eq $Name)
3870            {
3871                $TypeId = $TInfo->{"Fields"}{$N}{"Type"};
3872                last;
3873            }
3874        }
3875    }
3876   
3877    return $TypeId;
3878}
3879
3880sub writeReport($$)
3881{
3882    my ($Level, $Report) = @_;
3883    my $RPath = getReportPath($Level);
3884    writeFile($RPath, $Report);
3885}
3886
3887sub createReport()
3888{
3889    if($In::Opt{"JoinReport"}) {
3890        writeReport("Join", getReport("Join"));
3891    }
3892    elsif($In::Opt{"DoubleReport"})
3893    { # default
3894        writeReport("Binary", getReport("Binary"));
3895        writeReport("Source", getReport("Source"));
3896    }
3897    elsif($In::Opt{"BinaryOnly"})
3898    { # --binary
3899        writeReport("Binary", getReport("Binary"));
3900    }
3901    elsif($In::Opt{"SourceOnly"})
3902    { # --source
3903        writeReport("Source", getReport("Source"));
3904    }
3905}
3906
3907sub getCssStyles($)
3908{
3909    my $Level = $_[0];
3910   
3911    my $CssStyles = readModule("Css", "Report.css");
3912   
3913    if($Level eq "Join" or $In::Opt{"ExternCss"}) {
3914        $CssStyles .= readModule("Css", "Tabs.css");
3915    }
3916   
3917    return $CssStyles;
3918}
3919
3920sub getJsScript($)
3921{
3922    my $Level = $_[0];
3923   
3924    my $JScripts = readModule("Js", "Sections.js");
3925   
3926    if($Level eq "Join" or $In::Opt{"ExternJs"}) {
3927        $JScripts .= readModule("Js", "Tabs.js");
3928    }
3929   
3930    return $JScripts;
3931}
3932
3933sub getReport($)
3934{
3935    my $Level = $_[0];
3936   
3937    my $CssStyles = getCssStyles($Level);
3938    my $JScripts = getJsScript($Level);
3939   
3940    if(defined $In::Opt{"ExternCss"}) {
3941        writeFile($In::Opt{"ExternCss"}, $CssStyles);
3942    }
3943   
3944    if(defined $In::Opt{"ExternJs"}) {
3945        writeFile($In::Opt{"ExternJs"}, $JScripts);
3946    }
3947   
3948    if($Level eq "Join")
3949    {
3950        my $Title = $In::Opt{"TargetTitle"}.": ".$In::Desc{1}{"Version"}." to ".$In::Desc{2}{"Version"}." compatibility report";
3951        my $Keywords = $In::Opt{"TargetTitle"}.", compatibility";
3952        my $Description = "Compatibility report for the ".$In::Opt{"TargetTitle"}." library between ".$In::Desc{1}{"Version"}." and ".$In::Desc{2}{"Version"}." versions";
3953       
3954        my ($BSummary, $BMetaData, $BAnyChanged) = getSummary("Binary");
3955        my ($SSummary, $SMetaData, $SAnyChanged) = getSummary("Source");
3956       
3957        my $Report = "<!-\- $BMetaData -\->\n<!-\- $SMetaData -\->\n".composeHTML_Head($Level, $Title, $Keywords, $Description, $CssStyles, $JScripts, ($BAnyChanged or $SAnyChanged))."<body><a name='Source'></a><a name='Binary'></a><a name='Top'></a>";
3958       
3959        $Report .= getReportHeader("Join");
3960        $Report .= "<br/><div class='tabset'>\n";
3961        $Report .= "<a id='BinaryID' href='#BinaryTab' class='tab active'>Binary<br/>Compatibility</a>\n";
3962        $Report .= "<a id='SourceID' href='#SourceTab' style='margin-left:3px' class='tab disabled'>Source<br/>Compatibility</a>\n";
3963        $Report .= "</div>\n";
3964       
3965        $Report .= "<div id='BinaryTab' class='tab'>\n$BSummary\n".getReportAdded("Binary").getReportRemoved("Binary").getReportProblems("High", "Binary").getReportProblems("Medium", "Binary").getReportProblems("Low", "Binary").getReportProblems("Safe", "Binary").getSourceInfo()."<br/><br/><br/></div>";
3966       
3967        $Report .= "<div id='SourceTab' class='tab'>\n$SSummary\n".getReportAdded("Source").getReportRemoved("Source").getReportProblems("High", "Source").getReportProblems("Medium", "Source").getReportProblems("Low", "Source").getReportProblems("Safe", "Source").getSourceInfo()."<br/><br/><br/></div>";
3968       
3969        $Report .= getReportFooter();
3970        $Report .= "\n</body></html>";
3971        return $Report;
3972    }
3973    else
3974    {
3975        my ($Summary, $MetaData, $AnyChanged) = getSummary($Level);
3976       
3977        my $Title = $In::Opt{"TargetTitle"}.": ".$In::Desc{1}{"Version"}." to ".$In::Desc{2}{"Version"}." ".lc($Level)." compatibility report";
3978        my $Keywords = $In::Opt{"TargetTitle"}.", ".lc($Level).", compatibility";
3979        my $Description = "$Level compatibility report for the ".$In::Opt{"TargetTitle"}." library between ".$In::Desc{1}{"Version"}." and ".$In::Desc{2}{"Version"}." versions";
3980       
3981        my $Report = "<!-\- $MetaData -\->\n".composeHTML_Head($Level, $Title, $Keywords, $Description, $CssStyles, $JScripts, $AnyChanged)."<body><a name='Top'></a>";
3982        $Report .= getReportHeader($Level)."\n".$Summary."\n";
3983        $Report .= getReportAdded($Level).getReportRemoved($Level);
3984        $Report .= getReportProblems("High", $Level).getReportProblems("Medium", $Level).getReportProblems("Low", $Level).getReportProblems("Safe", $Level);
3985        $Report .= getSourceInfo()."<br/><br/><br/>\n";
3986        $Report .= getReportFooter();
3987        $Report .= "\n</body></html>";
3988        return $Report;
3989    }
3990}
3991
3992sub getReportFooter()
3993{
3994    my $Footer = "";
3995    $Footer .= "<hr/>";
3996    $Footer .= "<div class='footer' align='right'><i>Generated by ";
3997    $Footer .= "<a href='".$HomePage{"Dev"}."'>Java API Compliance Checker</a> $TOOL_VERSION &#160;";
3998    $Footer .= "</i></div>";
3999    $Footer .= "<br/>";
4000    return $Footer;
4001}
4002
4003sub getReportProblems($$)
4004{
4005    my ($Priority, $Level) = @_;
4006    my $Report = getReportTypeProblems($Priority, $Level);
4007    if(my $MProblems = getReportMethodProblems($Priority, $Level)) {
4008        $Report .= $MProblems;
4009    }
4010    if($Report)
4011    {
4012        if($In::Opt{"JoinReport"})
4013        {
4014            if($Priority eq "Safe") {
4015                $Report = "<a name=\'Other_".$Level."_Changes\'></a>".$Report;
4016            }
4017            else {
4018                $Report = "<a name=\'".$Priority."_Risk_".$Level."_Problems\'></a>".$Report;
4019            }
4020        }
4021        else
4022        {
4023            if($Priority eq "Safe") {
4024                $Report = "<a name=\'Other_Changes\'></a>".$Report;
4025            }
4026            else {
4027                $Report = "<a name=\'".$Priority."_Risk_Problems\'></a>".$Report;
4028            }
4029        }
4030    }
4031    return $Report;
4032}
4033
4034sub composeHTML_Head($$$$$$$)
4035{
4036    my ($Level, $Title, $Keywords, $Description, $Styles, $Scripts, $AnyChanged) = @_;
4037   
4038    my $Head = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
4039    $Head .= "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n";
4040    $Head .= "<head>\n";
4041    $Head .= "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
4042    $Head .= "<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n";
4043    $Head .= "<meta name=\"keywords\" content=\"$Keywords\" />\n";
4044    $Head .= "<meta name=\"description\" content=\"$Description\" />\n";
4045   
4046    if(not $AnyChanged) {
4047        $Head .= "<meta name=\"robots\" content=\"noindex\" />\n";
4048    }
4049   
4050    my $RPath = getReportPath($Level);
4051   
4052    if(defined $In::Opt{"ExternCss"}) {
4053        $Head .= "<link rel=\"stylesheet\" type=\"text/css\" href=\"".getRelPath($In::Opt{"ExternCss"}, $RPath)."\" />\n";
4054    }
4055   
4056    if(defined $In::Opt{"ExternJs"}) {
4057        $Head .= "<script type=\"text/javascript\" src=\"".getRelPath($In::Opt{"ExternJs"}, $RPath)."\"></script>\n";
4058    }
4059   
4060    $Head .= "<title>$Title</title>\n";
4061   
4062    if(not defined $In::Opt{"ExternCss"}) {
4063        $Head .= "<style type=\"text/css\">\n$Styles\n</style>\n";
4064    }
4065   
4066    if(not defined $In::Opt{"ExternJs"}) {
4067        $Head .= "<script type=\"text/javascript\" language=\"JavaScript\">\n<!--\n$Scripts\n-->\n</script>\n";
4068    }
4069   
4070    $Head .= "</head>\n";
4071   
4072    return $Head;
4073}
4074
4075sub insertIDs($)
4076{
4077    my $Text = $_[0];
4078    while($Text=~/CONTENT_ID/)
4079    {
4080        if(int($Content_Counter)%2)
4081        {
4082            $ContentID -= 1;
4083        }
4084        $Text=~s/CONTENT_ID/c_$ContentID/;
4085        $ContentID += 1;
4086        $Content_Counter += 1;
4087    }
4088    return $Text;
4089}
4090
4091sub registerUsage($$)
4092{
4093    my ($TypeId, $LVer) = @_;
4094    $Class_Constructed{$LVer}{$TypeId} = 1;
4095    if(my $BaseId = $TypeInfo{$LVer}{$TypeId}{"BaseType"}) {
4096        $Class_Constructed{$LVer}{$BaseId} = 1;
4097    }
4098}
4099
4100sub checkVoidMethod($)
4101{
4102    my $Method = $_[0];
4103   
4104    if($Method=~s/\)(.+)\Z/\)V/g) {
4105        return $Method;
4106    }
4107   
4108    return undef;
4109}
4110
4111sub detectAdded()
4112{
4113    foreach my $Method (keys(%{$MethodInfo{2}}))
4114    {
4115        if(not defined $MethodInfo{1}{$Method})
4116        {
4117            if(not methodFilter($Method, 2)) {
4118                next;
4119            }
4120           
4121            my $Class = getType($MethodInfo{2}{$Method}{"Class"}, 2);
4122           
4123            if(not defined $In::Opt{"CheckAnnotations"} and $Class->{"Annotation"}) {
4124                next;
4125            }
4126           
4127            $CheckedTypes{$Class->{"Name"}} = 1;
4128            $CheckedMethods{$Method} = 1;
4129           
4130            if(not $MethodInfo{2}{$Method}{"Constructor"}
4131            and my $Overridden = findMethod($Method, 2, $Class->{"Name"}, 2))
4132            {
4133                if(defined $MethodInfo{1}{$Overridden}
4134                and $Class->{"Type"} eq "class"
4135                and ($TName_Tid{1}{$Class->{"Name"}} or $TName_Tid_Generic{1}{getGeneric($Class->{"Name"})}))
4136                { # class should exist in previous version
4137                    %{$CompatProblems{$Overridden}{"Class_Overridden_Method"}{"this.".getSFormat($Method)}}=(
4138                        "Type_Name"=>$Class->{"Name"},
4139                        "Target"=>$MethodInfo{2}{$Method}{"Signature"},
4140                        "Old_Value"=>$Overridden,
4141                        "New_Value"=>$Method);
4142                }
4143            }
4144            if($MethodInfo{2}{$Method}{"Abstract"}) {
4145                $AddedMethod_Abstract{$Class->{"Name"}}{$Method} = 1;
4146            }
4147           
4148            if(not ($MethodInfo{2}{$Method}{"Access"} eq "protected" and $Class->{"Final"})) {
4149                %{$CompatProblems{$Method}{"Added_Method"}{""}} = ();
4150            }
4151           
4152            if(not $MethodInfo{2}{$Method}{"Constructor"})
4153            {
4154                my $VoidMethod = checkVoidMethod($Method);
4155                my $ReturnType = getTypeName($MethodInfo{2}{$Method}{"Return"}, 2);
4156               
4157                if(defined $Class->{"GenericParam"}
4158                and defined $Class->{"GenericParam"}{$ReturnType}) {
4159                    $ReturnType = getTypeName($Class->{"GenericParam"}{$ReturnType}, 2);
4160                }
4161               
4162                if(defined $MethodInfo{1}{$VoidMethod}
4163                and $ReturnType ne "void")
4164                { # return value type changed from void
4165                    $ChangedReturnFromVoid{$VoidMethod} = 1;
4166                    $ChangedReturnFromVoid{$Method} = 1;
4167                   
4168                    %{$CompatProblems{$VoidMethod}{"Changed_Method_Return_From_Void"}{""}}=(
4169                        "New_Value"=>getTypeName($MethodInfo{2}{$Method}{"Return"}, 2)
4170                    );
4171                }
4172                elsif(my $OldMethod = $OldMethodSignature{getBaseSignature($Method)})
4173                {
4174                    if($OldMethod ne $Method)
4175                    {
4176                        my $OldReturnType = getTypeName($MethodInfo{1}{$OldMethod}{"Return"}, 1);
4177                       
4178                        %{$CompatProblems{$OldMethod}{"Changed_Method_Return"}{""}}=(
4179                            "Old_Value"=>$OldReturnType,
4180                            "New_Value"=>$ReturnType
4181                        );
4182                    }
4183                }
4184            }
4185        }
4186    }
4187}
4188
4189sub detectRemoved()
4190{
4191    foreach my $Method (keys(%{$MethodInfo{1}}))
4192    {
4193        if(not defined $MethodInfo{2}{$Method})
4194        {
4195            if(not methodFilter($Method, 1)) {
4196                next;
4197            }
4198           
4199            my $Class = getType($MethodInfo{1}{$Method}{"Class"}, 1);
4200           
4201            if(not defined $In::Opt{"CheckAnnotations"} and $Class->{"Annotation"}) {
4202                next;
4203            }
4204           
4205            $CheckedTypes{$Class->{"Name"}} = 1;
4206            $CheckedMethods{$Method} = 1;
4207           
4208            if(not $MethodInfo{1}{$Method}{"Constructor"}
4209            and my $MovedUp = findMethod($Method, 1, $Class->{"Name"}, 2))
4210            {
4211                if($Class->{"Type"} eq "class"
4212                and not $MethodInfo{1}{$Method}{"Abstract"}
4213                and ($TName_Tid{2}{$Class->{"Name"}} or $TName_Tid_Generic{2}{getGeneric($Class->{"Name"})}))
4214                {# class should exist in newer version
4215                    %{$CompatProblems{$Method}{"Class_Method_Moved_Up_Hierarchy"}{"this.".getSFormat($MovedUp)}}=(
4216                        "Type_Name"=>$Class->{"Name"},
4217                        "Target"=>$MethodInfo{2}{$MovedUp}{"Signature"},
4218                        "Old_Value"=>$Method,
4219                        "New_Value"=>$MovedUp);
4220                }
4221            }
4222            else
4223            {
4224                if($MethodInfo{1}{$Method}{"Abstract"}) {
4225                    $RemovedMethod_Abstract{$Class->{"Name"}}{$Method} = 1;
4226                }
4227               
4228                if(not ($MethodInfo{1}{$Method}{"Access"} eq "protected" and $Class->{"Final"})) {
4229                    %{$CompatProblems{$Method}{"Removed_Method"}{""}} = ();
4230                }
4231            }
4232        }
4233    }
4234}
4235
4236sub isCyclical($$) {
4237    return (grep {$_ eq $_[1]} @{$_[0]});
4238}
4239
4240sub mergeAPIs($$)
4241{
4242    my ($LVer, $Dep) = @_;
4243   
4244    foreach my $TId (keys(%{$Dep->{"TypeInfo"}}))
4245    {
4246        $TypeInfo{$LVer}{$TId} = $Dep->{"TypeInfo"}{$TId};
4247        $TypeInfo{$LVer}{$TId}{"Dep"} = 1;
4248    }
4249   
4250    my $MInfo = $Dep->{"MethodInfo"};
4251    foreach my $M_Id (keys(%{$MInfo}))
4252    {
4253        if(my $Name = $MInfo->{$M_Id}{"Name"})
4254        {
4255            $MethodInfo{$LVer}{$Name} = $MInfo->{$M_Id};
4256            $MethodInfo{$LVer}{$Name}{"Dep"} = 1;
4257        }
4258    }
4259}
4260
4261sub readAPIDump($$$)
4262{
4263    my ($LVer, $Path, $Subj) = @_;
4264   
4265    if(not $In::Opt{"CountMethods"}) {
4266        printMsg("INFO", "Reading API dump ($LVer) ...");
4267    }
4268   
4269    my $FilePath = "";
4270    if(isDump_U($Path))
4271    { # input *.dump
4272        $FilePath = $Path;
4273    }
4274    else
4275    { # input *.dump.tar.gz
4276        $FilePath = unpackDump($Path);
4277        if(not isDump_U($FilePath)) {
4278            exitStatus("Invalid_Dump", "specified API dump \'$Path\' is not valid, try to recreate it");
4279        }
4280    }
4281   
4282    my $APIRef = {};
4283   
4284    open(DUMP, $FilePath);
4285    local $/ = undef;
4286    my $Content = <DUMP>;
4287    close(DUMP);
4288   
4289    if(getDirname($FilePath) eq $In::Opt{"Tmp"}."/unpack")
4290    { # remove temp file
4291        unlink($FilePath);
4292    }
4293   
4294    if($Content!~/};\s*\Z/) {
4295        exitStatus("Invalid_Dump", "specified API dump \'$Path\' is not valid, try to recreate it");
4296    }
4297   
4298    $APIRef = eval($Content);
4299   
4300    if(not $APIRef) {
4301        exitStatus("Error", "internal error - eval() procedure seem to not working correctly, try to remove 'use strict' and try again");
4302    }
4303   
4304    my $APIVer = $APIRef->{"API_DUMP_VERSION"};
4305   
4306    if($APIVer)
4307    {
4308        if(cmpVersions($APIVer, $API_DUMP_VERSION)>0)
4309        { # future formats
4310            exitStatus("Dump_Version", "version of the API dump is newer than version of the tool");
4311        }
4312       
4313        if(cmpVersions($APIVer, $API_DUMP_VERSION)<0)
4314        { # old formats
4315            printMsg("WARNING", "version of the API dump is older than version of the tool");
4316        }
4317    }
4318   
4319    if(cmpVersions($APIVer, $API_DUMP_VERSION_MIN)<0)
4320    { # obsolete formats
4321        exitStatus("Dump_Version", "version of the API dump is too old and unsupported anymore, please regenerate it");
4322    }
4323   
4324    if($Subj ne "Dep")
4325    {
4326        $In::Desc{$LVer}{"Version"} = $APIRef->{"LibraryVersion"};
4327        $In::Desc{$LVer}{"Dump"} = 1;
4328    }
4329   
4330    return $APIRef;
4331}
4332
4333sub checkVersionNum($$)
4334{
4335    my ($LVer, $Path) = @_;
4336   
4337    if($In::Desc{$LVer}{"TargetVersion"}) {
4338        return;
4339    }
4340   
4341    if($Path!~/\.(jar|jmod)\Z/i) {
4342        return;
4343    }
4344   
4345    my $Ver = undef;
4346   
4347    if($Path=~/\.jar\Z/i)
4348    {
4349        if(not defined $Ver) {
4350            $Ver = getManifestVersion(getAbsPath($Path));
4351        }
4352    }
4353   
4354    if(not defined $Ver) {
4355        $Ver = getPkgVersion(getFilename($Path));
4356    }
4357   
4358    if(not defined $Ver) {
4359        $Ver = parseVersion($Path);
4360    }
4361   
4362    if(not defined $Ver)
4363    {
4364        if($In::Opt{"DumpAPI"})
4365        {
4366            $Ver = "XYZ";
4367        }
4368        else
4369        {
4370            if($LVer==1) {
4371                $Ver = "X";
4372            }
4373            else {
4374                $Ver = "Y";
4375            }
4376        }
4377    }
4378   
4379    $In::Desc{$LVer}{"TargetVersion"} = $Ver;
4380   
4381    if($In::Opt{"DumpAPI"}) {
4382        printMsg("WARNING", "set version number to $Ver (use -vnum option to change it)");
4383    }
4384    else {
4385        printMsg("WARNING", "set #$LVer version number to $Ver (use --v$LVer=NUM option to change it)");
4386    }
4387}
4388
4389sub getManifestVersion($)
4390{
4391    my $Path = $_[0];
4392   
4393    my $JarCmd = getCmdPath("jar");
4394    if(not $JarCmd) {
4395        exitStatus("Not_Found", "can't find \"jar\" command");
4396    }
4397   
4398    my $TmpDir = $In::Opt{"Tmp"};
4399   
4400    chdir($TmpDir);
4401    system($JarCmd." -xf \"$Path\" META-INF 2>null");
4402    chdir($In::Opt{"OrigDir"});
4403   
4404    my $Manifest = $TmpDir."/META-INF/MANIFEST.MF";
4405   
4406    if(-f $Manifest)
4407    {
4408        if(my $Content = readFile($Manifest))
4409        {
4410            if($Content=~/(\A|\s)Implementation\-Version:\s*(.+)(\s|\Z)/i) {
4411                return $2;
4412            }
4413        }
4414    }
4415    return undef;
4416}
4417
4418sub parseVersion($)
4419{
4420    my $Str = $_[0];
4421   
4422    if(not $Str) {
4423        return undef;
4424    }
4425   
4426    if($Str=~/(\/|\\|\w|\A)[\-\_]*(\d+[\d\.\-]+\d+|\d+)/) {
4427        return $2;
4428    }
4429    return undef;
4430}
4431
4432sub getPkgVersion($)
4433{
4434    my $Name = $_[0];
4435    $Name=~s/\.\w+\Z//;
4436    if($Name=~/\A(.+[a-z])[\-\_](v|ver|)(\d.+?)\Z/i)
4437    { # libsample-N
4438      # libsample-vN
4439        return ($1, $3);
4440    }
4441    elsif($Name=~/\A(.+?)(\d[\d\.]*)\Z/i)
4442    { # libsampleN
4443        return ($1, $2);
4444    }
4445    elsif($Name=~/\A(.+)[\-\_](v|ver|)(\d.+?)\Z/i)
4446    { # libsample-N
4447      # libsample-vN
4448        return ($1, $3);
4449    }
4450    elsif($Name=~/\A([a-z_\-]+)(\d.+?)\Z/i)
4451    { # libsampleNb
4452        return ($1, $2);
4453    }
4454    return (undef, undef);
4455}
4456
4457sub dumpSorting($)
4458{
4459    my $Hash = $_[0];
4460    return [] if(not $Hash);
4461    my @Keys = keys(%{$Hash});
4462    return [] if($#Keys<0);
4463    if($Keys[0]=~/\A\d+\Z/)
4464    { # numbers
4465        return [sort {int($a)<=>int($b)} @Keys];
4466    }
4467    else
4468    { # strings
4469        return [sort {$a cmp $b} @Keys];
4470    }
4471}
4472
4473sub printStatMsg($)
4474{
4475    my $Level = $_[0];
4476    printMsg("INFO", "Total ".lc($Level)." compatibility problems: ".$RESULT{$Level}{"Problems"}.", warnings: ".$RESULT{$Level}{"Warnings"});
4477}
4478
4479sub printReport()
4480{
4481    printMsg("INFO", "Creating compatibility report ...");
4482    createReport();
4483    if($In::Opt{"JoinReport"} or $In::Opt{"DoubleReport"})
4484    {
4485        if($RESULT{"Binary"}{"Problems"}
4486        or $RESULT{"Source"}{"Problems"})
4487        {
4488            printMsg("INFO", "Binary compatibility: ".(100-$RESULT{"Binary"}{"Affected"})."\%");
4489            printMsg("INFO", "Source compatibility: ".(100-$RESULT{"Source"}{"Affected"})."\%");
4490        }
4491        else
4492        {
4493            printMsg("INFO", "Binary compatibility: 100\%");
4494            printMsg("INFO", "Source compatibility: 100\%");
4495        }
4496        printStatMsg("Binary");
4497        printStatMsg("Source");
4498    }
4499    elsif($In::Opt{"BinaryOnly"})
4500    {
4501        if($RESULT{"Binary"}{"Problems"}) {
4502            printMsg("INFO", "Binary compatibility: ".(100-$RESULT{"Binary"}{"Affected"})."\%");
4503        }
4504        else {
4505            printMsg("INFO", "Binary compatibility: 100\%");
4506        }
4507        printStatMsg("Binary");
4508    }
4509    elsif($In::Opt{"SourceOnly"})
4510    {
4511        if($RESULT{"Source"}{"Problems"}) {
4512            printMsg("INFO", "Source compatibility: ".(100-$RESULT{"Source"}{"Affected"})."\%");
4513        }
4514        else {
4515            printMsg("INFO", "Source compatibility: 100\%");
4516        }
4517        printStatMsg("Source");
4518    }
4519    if($In::Opt{"JoinReport"})
4520    {
4521        printMsg("INFO", "Report: ".getReportPath("Join"));
4522    }
4523    elsif($In::Opt{"DoubleReport"})
4524    { # default
4525        printMsg("INFO", "Report (BC): ".getReportPath("Binary"));
4526        printMsg("INFO", "Report (SC): ".getReportPath("Source"));
4527    }
4528    elsif($In::Opt{"BinaryOnly"})
4529    { # --binary
4530        printMsg("INFO", "Report: ".getReportPath("Binary"));
4531    }
4532    elsif($In::Opt{"SourceOnly"})
4533    { # --source
4534        printMsg("INFO", "Report: ".getReportPath("Source"));
4535    }
4536}
4537
4538sub getReportPath($)
4539{
4540    my $Level = $_[0];
4541    my $Dir = "compat_reports/".$In::Opt{"TargetLib"}."/".$In::Desc{1}{"Version"}."_to_".$In::Desc{2}{"Version"};
4542    if($Level eq "Binary")
4543    {
4544        if($In::Opt{"BinaryReportPath"})
4545        { # --bin-report-path
4546            return $In::Opt{"BinaryReportPath"};
4547        }
4548        elsif($In::Opt{"OutputReportPath"})
4549        { # --report-path
4550            return $In::Opt{"OutputReportPath"};
4551        }
4552        else
4553        { # default
4554            return $Dir."/bin_compat_report.html";
4555        }
4556    }
4557    elsif($Level eq "Source")
4558    {
4559        if($In::Opt{"SourceReportPath"})
4560        { # --src-report-path
4561            return $In::Opt{"SourceReportPath"};
4562        }
4563        elsif($In::Opt{"OutputReportPath"})
4564        { # --report-path
4565            return $In::Opt{"OutputReportPath"};
4566        }
4567        else
4568        { # default
4569            return $Dir."/src_compat_report.html";
4570        }
4571    }
4572    else
4573    {
4574        if($In::Opt{"OutputReportPath"})
4575        { # --report-path
4576            return $In::Opt{"OutputReportPath"};
4577        }
4578        else
4579        { # default
4580            return $Dir."/compat_report.html";
4581        }
4582    }
4583}
4584
4585sub unpackDump($)
4586{
4587    my $Path = $_[0];
4588   
4589    if(isDump_U($Path)) {
4590        return $Path;
4591    }
4592   
4593    my $TmpDir = $In::Opt{"Tmp"};
4594   
4595    $Path = getAbsPath($Path);
4596    $Path = pathFmt($Path);
4597   
4598    my ($Dir, $FileName) = sepPath($Path);
4599    my $UnpackDir = $TmpDir."/unpack";
4600    if(-d $UnpackDir) {
4601        rmtree($UnpackDir);
4602    }
4603    mkpath($UnpackDir);
4604   
4605    if($FileName=~s/\Q.zip\E\Z//g)
4606    { # *.zip
4607        my $UnzipCmd = getCmdPath("unzip");
4608        if(not $UnzipCmd) {
4609            exitStatus("Not_Found", "can't find \"unzip\" command");
4610        }
4611        chdir($UnpackDir);
4612        system("$UnzipCmd \"$Path\" >contents.txt");
4613        chdir($In::Opt{"OrigDir"});
4614        if($?) {
4615            exitStatus("Error", "can't extract \'$Path\'");
4616        }
4617       
4618        my @Contents = ();
4619        foreach (split("\n", readFile("$UnpackDir/contents.txt")))
4620        {
4621            if(/inflating:\s*([^\s]+)/) {
4622                push(@Contents, $1);
4623            }
4624        }
4625        if(not @Contents) {
4626            exitStatus("Error", "can't extract \'$Path\'");
4627        }
4628        return join_P($UnpackDir, $Contents[0]);
4629    }
4630    elsif($FileName=~s/\Q.tar.gz\E\Z//g)
4631    { # *.tar.gz
4632        if($In::Opt{"OS"} eq "windows")
4633        { # -xvzf option is not implemented in tar.exe (2003)
4634          # use "gzip.exe -k -d -f" + "tar.exe -xvf" instead
4635            my $TarCmd = getCmdPath("tar");
4636            if(not $TarCmd) {
4637                exitStatus("Not_Found", "can't find \"tar\" command");
4638            }
4639            my $GzipCmd = getCmdPath("gzip");
4640            if(not $GzipCmd) {
4641                exitStatus("Not_Found", "can't find \"gzip\" command");
4642            }
4643            chdir($UnpackDir);
4644            qx/$GzipCmd -k -d -f "$Path"/; # keep input files (-k)
4645            if($?) {
4646                exitStatus("Error", "can't extract \'$Path\'");
4647            }
4648            my @Contents = qx/$TarCmd -xvf "$Dir\\$FileName.tar"/;
4649            chdir($In::Opt{"OrigDir"});
4650            if($? or not @Contents) {
4651                exitStatus("Error", "can't extract \'$Path\'");
4652            }
4653            unlink($Dir."/".$FileName.".tar");
4654            chomp $Contents[0];
4655            return join_P($UnpackDir, $Contents[0]);
4656        }
4657        else
4658        { # Linux, Unix, OS X
4659            my $TarCmd = getCmdPath("tar");
4660            if(not $TarCmd) {
4661                exitStatus("Not_Found", "can't find \"tar\" command");
4662            }
4663            chdir($UnpackDir);
4664            my @Contents = qx/$TarCmd -xvzf "$Path" 2>&1/;
4665            chdir($In::Opt{"OrigDir"});
4666            if($? or not @Contents) {
4667                exitStatus("Error", "can't extract \'$Path\'");
4668            }
4669            $Contents[0]=~s/^x //; # OS X
4670            chomp $Contents[0];
4671            return join_P($UnpackDir, $Contents[0]);
4672        }
4673    }
4674}
4675
4676sub createArchive($$)
4677{
4678    my ($Path, $To) = @_;
4679    if(not $To) {
4680        $To = ".";
4681    }
4682   
4683    my $TmpDir = $In::Opt{"Tmp"};
4684   
4685    my ($From, $Name) = sepPath($Path);
4686    if($In::Opt{"OS"} eq "windows")
4687    { # *.zip
4688        my $ZipCmd = getCmdPath("zip");
4689        if(not $ZipCmd) {
4690            exitStatus("Not_Found", "can't find \"zip\"");
4691        }
4692        my $Pkg = $To."/".$Name.".zip";
4693        unlink($Pkg);
4694        chdir($To);
4695        system("$ZipCmd -j \"$Name.zip\" \"$Path\" >\"$TmpDir/null\"");
4696        if($?)
4697        { # cannot allocate memory (or other problems with "zip")
4698            chdir($In::Opt{"OrigDir"});
4699            exitStatus("Error", "can't pack the API dump: ".$!);
4700        }
4701        chdir($In::Opt{"OrigDir"});
4702        unlink($Path);
4703        return $Pkg;
4704    }
4705    else
4706    { # *.tar.gz
4707        my $TarCmd = getCmdPath("tar");
4708        if(not $TarCmd) {
4709            exitStatus("Not_Found", "can't find \"tar\"");
4710        }
4711        my $GzipCmd = getCmdPath("gzip");
4712        if(not $GzipCmd) {
4713            exitStatus("Not_Found", "can't find \"gzip\"");
4714        }
4715        my $Pkg = abs_path($To)."/".$Name.".tar.gz";
4716        if(-e $Pkg) {
4717            unlink($Pkg);
4718        }
4719        system($TarCmd, "-C", $From, "-czf", $Pkg, $Name);
4720        if($?)
4721        { # cannot allocate memory (or other problems with "tar")
4722            exitStatus("Error", "can't pack the API dump: ".$!);
4723        }
4724        unlink($Path);
4725        return $To."/".$Name.".tar.gz";
4726    }
4727}
4728
4729sub initAliases($)
4730{
4731    my $LVer = $_[0];
4732   
4733    initAPI($LVer);
4734   
4735    $MethodInfo{$LVer} = $In::API{$LVer}{"MethodInfo"};
4736    $TypeInfo{$LVer} = $In::API{$LVer}{"TypeInfo"};
4737    $TName_Tid{$LVer} = $In::API{$LVer}{"TName_Tid"};
4738   
4739    initAliases_TypeAttr($LVer);
4740}
4741
4742sub createAPIFile($$)
4743{
4744    my ($LVer, $DescPath) = @_;
4745   
4746    if(not -e $DescPath) {
4747        exitStatus("Access_Error", "can't access \'$DescPath\'");
4748    }
4749   
4750    detectDefaultPaths("bin", "java");
4751   
4752    if(isDump($DescPath))
4753    {
4754        $In::API{$LVer} = readAPIDump($LVer, $DescPath, "Main");
4755        initAliases($LVer);
4756       
4757        if(my $V = $In::Desc{$LVer}{"TargetVersion"}) {
4758            $In::Desc{$LVer}{"Version"} = $V;
4759        }
4760        else {
4761            $In::Desc{$LVer}{"Version"} = $In::API{$LVer}{"LibraryVersion"};
4762        }
4763    }
4764    else
4765    {
4766        loadModule("APIDump");
4767   
4768        checkVersionNum($LVer, $DescPath);
4769        readDesc(createDesc($DescPath, $LVer), $LVer);
4770       
4771        initLogging($LVer);
4772       
4773        createAPIDump($LVer);
4774    }
4775   
4776    if(my $Dep = $In::Desc{$LVer}{"DepDump"}) {
4777        mergeAPIs($LVer, readAPIDump($LVer, $Dep, "Dep"));
4778    }
4779   
4780    printMsg("INFO", "Creating library API dump ...");
4781   
4782    $In::API{$LVer}{"API_DUMP_VERSION"} = $API_DUMP_VERSION;
4783    $In::API{$LVer}{"JAPI_COMPLIANCE_CHECKER_VERSION"} = $TOOL_VERSION;
4784   
4785    foreach ("TName_Tid") {
4786        delete($In::API{$LVer}{$_});
4787    }
4788   
4789    my $DumpPath = "api_dumps/".$In::Opt{"TargetLib"}."/".$In::Desc{$LVer}{"Version"}."/API.dump";
4790    if($In::Opt{"OutputDumpPath"})
4791    { # user defined path
4792        $DumpPath = $In::Opt{"OutputDumpPath"};
4793    }
4794   
4795    my $ArExt = $In::Opt{"Ar"};
4796    my $Archive = ($DumpPath=~s/\Q.$ArExt\E\Z//g);
4797   
4798    if($Archive)
4799    {
4800        my $TarCmd = getCmdPath("tar");
4801        if(not $TarCmd) {
4802            exitStatus("Not_Found", "can't find \"tar\"");
4803        }
4804        my $GzipCmd = getCmdPath("gzip");
4805        if(not $GzipCmd) {
4806            exitStatus("Not_Found", "can't find \"gzip\"");
4807        }
4808    }
4809   
4810    my ($DDir, $DName) = sepPath($DumpPath);
4811    my $DPath = $In::Opt{"Tmp"}."/".$DName;
4812    if(not $Archive) {
4813        $DPath = $DumpPath;
4814    }
4815   
4816    mkpath($DDir);
4817   
4818    open(DUMP, ">", $DPath) || die ("can't open file \'$DPath\': $!\n");
4819    print DUMP Dumper($In::API{$LVer});
4820    close(DUMP);
4821   
4822    if(not -s $DPath) {
4823        exitStatus("Error", "can't create API dump because something is going wrong with the Data::Dumper module");
4824    }
4825   
4826    if($Archive) {
4827        $DumpPath = createArchive($DPath, $DDir);
4828    }
4829   
4830    if($In::Opt{"OutputDumpPath"}) {
4831        printMsg("INFO", "Dump path: ".$In::Opt{"OutputDumpPath"});
4832    }
4833    else {
4834        printMsg("INFO", "Dump path: $DumpPath");
4835    }
4836    exit(0);
4837}
4838
4839sub compareInit()
4840{
4841    if(not $In::Desc{1}{"Path"}) {
4842        exitStatus("Error", "-old option is not specified");
4843    }
4844    if(not -e $In::Desc{1}{"Path"}) {
4845        exitStatus("Access_Error", "can't access \'".$In::Desc{1}{"Path"}."\'");
4846    }
4847    if(not $In::Desc{2}{"Path"}) {
4848        exitStatus("Error", "-new option is not specified");
4849    }
4850    if(not -e $In::Desc{2}{"Path"}) {
4851        exitStatus("Access_Error", "can't access \'".$In::Desc{2}{"Path"}."\'");
4852    }
4853   
4854    if($In::Opt{"Quick"})
4855    {
4856        $CompatRules{"Binary"}{"Interface_Added_Super_Interface"}{"Severity"} = "Low";
4857        $CompatRules{"Binary"}{"Abstract_Class_Added_Super_Abstract_Class"}{"Severity"} = "Low";
4858        $CompatRules{"Binary"}{"Abstract_Class_Added_Super_Interface"}{"Severity"} = "Low";
4859        $CompatRules{"Binary"}{"Abstract_Class_Added_Abstract_Method"}{"Severity"} = "Low";
4860        $CompatRules{"Binary"}{"Interface_Added_Abstract_Method"}{"Severity"} = "Low";
4861    }
4862   
4863    printMsg("INFO", "Preparing, please wait ...");
4864   
4865    detectDefaultPaths("bin", undef);
4866   
4867    if(isDump($In::Desc{1}{"Path"}))
4868    {
4869        $In::API{1} = readAPIDump(1, $In::Desc{1}{"Path"}, "Main");
4870        initAliases(1);
4871       
4872        if(my $V = $In::Desc{1}{"TargetVersion"}) {
4873            $In::Desc{1}{"Version"} = $V;
4874        }
4875        else {
4876            $In::Desc{1}{"Version"} = $In::API{1}{"LibraryVersion"};
4877        }
4878    }
4879    else
4880    {
4881        loadModule("APIDump");
4882       
4883        checkVersionNum(1, $In::Desc{1}{"Path"});
4884        readDesc(createDesc($In::Desc{1}{"Path"}, 1), 1);
4885       
4886        initLogging(1);
4887        detectDefaultPaths(undef, "java");
4888        createAPIDump(1);
4889    }
4890   
4891    if(my $Dep = $In::Desc{1}{"DepDump"}) {
4892        mergeAPIs(1, readAPIDump(1, $Dep, "Dep"));
4893    }
4894   
4895    if(isDump($In::Desc{2}{"Path"}))
4896    {
4897        $In::API{2} = readAPIDump(2, $In::Desc{2}{"Path"}, "Main");
4898        initAliases(2);
4899       
4900        if(my $V = $In::Desc{2}{"TargetVersion"}) {
4901            $In::Desc{2}{"Version"} = $V;
4902        }
4903        else {
4904            $In::Desc{2}{"Version"} = $In::API{2}{"LibraryVersion"};
4905        }
4906    }
4907    else
4908    {
4909        loadModule("APIDump");
4910       
4911        checkVersionNum(2, $In::Desc{2}{"Path"});
4912        readDesc(createDesc($In::Desc{2}{"Path"}, 2), 2);
4913       
4914        initLogging(2);
4915        detectDefaultPaths(undef, "java");
4916        createAPIDump(2);
4917    }
4918   
4919    if(my $Dep = $In::Desc{2}{"DepDump"}) {
4920        mergeAPIs(2, readAPIDump(2, $Dep, "Dep"));
4921    }
4922   
4923    prepareData(1);
4924    prepareData(2);
4925   
4926    foreach my $Inv (keys(%{$MethodUsed{2}}))
4927    {
4928        foreach my $M (keys(%{$MethodUsed{2}{$Inv}}))
4929        {
4930            my $InvType = $MethodUsed{2}{$Inv}{$M};
4931           
4932            if($InvType ne "static"
4933            and index($Inv, "<init>")==-1)
4934            {
4935                my $CName = $Inv;
4936                $CName=~s/\A\"\[L(.+);"/$1/g;
4937                $CName=~s/#/./g;
4938               
4939                if($CName=~/\A(.+?)\./)
4940                {
4941                    $CName = $1;
4942                    if($CName!~/\"/)
4943                    {
4944                        $CName=~s!/!.!g;
4945                        $ClassMethod_AddedUsed{$CName}{$Inv} = $M;
4946                    }
4947                }
4948            }
4949           
4950            if(not defined $MethodInfo{1}{$M}) {
4951                delete($MethodUsed{2}{$Inv}{$M});
4952            }
4953        }
4954    }
4955   
4956    foreach my $ClassName (keys(%ClassMethod_AddedUsed))
4957    {
4958        foreach my $MethodName (keys(%{$ClassMethod_AddedUsed{$ClassName}}))
4959        {
4960            if(defined $MethodInfo{1}{$MethodName}
4961            or defined $MethodInfo{2}{$MethodName}
4962            or defined $MethodUsed{1}{$MethodName}
4963            or findMethod($MethodName, 2, $ClassName, 1))
4964            { # abstract method added by the new super-class (abstract) or super-interface
4965                delete($ClassMethod_AddedUsed{$ClassName}{$MethodName});
4966            }
4967        }
4968        if(not keys(%{$ClassMethod_AddedUsed{$ClassName}})) {
4969            delete($ClassMethod_AddedUsed{$ClassName});
4970        }
4971    }
4972}
4973
4974sub scenario()
4975{
4976    setTarget("default");
4977   
4978    initAliases(1);
4979    initAliases(2);
4980   
4981    $In::Opt{"OrigDir"} = cwd();
4982    $In::Opt{"Tmp"} = tempdir(CLEANUP=>1);
4983    $In::Opt{"Reproducible"} = 1;
4984   
4985    $In::Opt{"JoinReport"} = 1;
4986    $In::Opt{"DoubleReport"} = 0;
4987   
4988    if($In::Opt{"BinaryOnly"} and $In::Opt{"SourceOnly"})
4989    { # both --binary and --source
4990      # is the default mode
4991        $In::Opt{"DoubleReport"} = 1;
4992        $In::Opt{"JoinReport"} = 0;
4993        $In::Opt{"BinaryOnly"} = 0;
4994        $In::Opt{"SourceOnly"} = 0;
4995        if($In::Opt{"OutputReportPath"})
4996        { # --report-path
4997            $In::Opt{"DoubleReport"} = 0;
4998            $In::Opt{"JoinReport"} = 1;
4999        }
5000    }
5001    elsif($In::Opt{"BinaryOnly"} or $In::Opt{"SourceOnly"})
5002    { # --binary or --source
5003        $In::Opt{"DoubleReport"} = 0;
5004        $In::Opt{"JoinReport"} = 0;
5005    }
5006    if(defined $In::Opt{"Help"})
5007    {
5008        helpMsg();
5009        exit(0);
5010    }
5011    if(defined $In::Opt{"ShowVersion"})
5012    {
5013        printMsg("INFO", "Java API Compliance Checker (JAPICC) $TOOL_VERSION\nCopyright (C) 2018 Andrey Ponomarenko's ABI Laboratory\nLicense: LGPLv2.1+ <http://www.gnu.org/licenses/>\nThis program is free software: you can redistribute it and/or modify it.\n\nWritten by Andrey Ponomarenko.");
5014        exit(0);
5015    }
5016    if(defined $In::Opt{"DumpVersion"})
5017    {
5018        printMsg("INFO", $TOOL_VERSION);
5019        exit(0);
5020    }
5021    $Data::Dumper::Sortkeys = 1;
5022   
5023    # FIXME: can't pass \&dumpSorting - cause a segfault sometimes
5024    if($In::Opt{"SortDump"})
5025    {
5026        $Data::Dumper::Useperl = 1;
5027        $Data::Dumper::Sortkeys = \&dumpSorting;
5028    }
5029   
5030    if(defined $In::Opt{"TestTool"})
5031    {
5032        detectDefaultPaths("bin", "java");
5033        loadModule("RegTests");
5034        testTool();
5035        exit(0);
5036    }
5037   
5038    if(defined $In::Opt{"ShortMode"})
5039    {
5040        if(not defined $In::Opt{"AffectLimit"}) {
5041            $In::Opt{"AffectLimit"} = 10;
5042        }
5043    }
5044   
5045    if(not $In::Opt{"TargetLib"} and not $In::Opt{"CountMethods"})
5046    {
5047        if($In::Opt{"DumpAPI"})
5048        {
5049            if($In::Opt{"DumpAPI"}=~/\.(jar|jmod)\Z/)
5050            { # short usage
5051                my ($Name, $Version) = getPkgVersion(getFilename($In::Opt{"DumpAPI"}));
5052                if($Name and $Version ne "")
5053                {
5054                    $In::Opt{"TargetLib"} = $Name;
5055                    if(not $In::Desc{1}{"TargetVersion"}) {
5056                        $In::Desc{1}{"TargetVersion"} = $Version;
5057                    }
5058                }
5059            }
5060        }
5061        else
5062        {
5063            if($In::Desc{1}{"Path"}=~/\.(jar|jmod)\Z/ and $In::Desc{2}{"Path"}=~/\.(jar|jmod)\Z/)
5064            { # short usage
5065                my ($Name1, $Version1) = getPkgVersion(getFilename($In::Desc{1}{"Path"}));
5066                my ($Name2, $Version2) = getPkgVersion(getFilename($In::Desc{2}{"Path"}));
5067               
5068                if($Name1 and $Version1 ne ""
5069                and $Version2 ne "")
5070                {
5071                    $In::Opt{"TargetLib"} = $Name1;
5072                    if(not $In::Desc{1}{"TargetVersion"}) {
5073                        $In::Desc{1}{"TargetVersion"} = $Version1;
5074                    }
5075                    if(not $In::Desc{2}{"TargetVersion"}) {
5076                        $In::Desc{2}{"TargetVersion"} = $Version2;
5077                    }
5078                }
5079            }
5080        }
5081       
5082        if(not $In::Opt{"TargetLib"}) {
5083            exitStatus("Error", "library name is not selected (option --lib=NAME)");
5084        }
5085    }
5086    else
5087    { # validate library name
5088        if($In::Opt{"TargetLib"}=~/[\*\/\\]/) {
5089            exitStatus("Error", "\"\\\", \"\/\" and \"*\" symbols are not allowed in the library name");
5090        }
5091    }
5092    if(not $In::Opt{"TargetTitle"}) {
5093        $In::Opt{"TargetTitle"} = $In::Opt{"TargetLib"};
5094    }
5095    if(my $ClassListPath = $In::Opt{"ClassListPath"})
5096    {
5097        if(not -f $ClassListPath) {
5098            exitStatus("Access_Error", "can't access file \'$ClassListPath\'");
5099        }
5100        foreach my $Class (split(/\n/, readFile($ClassListPath)))
5101        {
5102            $Class=~s/\//./g;
5103            $In::Opt{"ClassList_User"}{$Class} = 1;
5104        }
5105    }
5106    if(my $AnnotationsListPath = $In::Opt{"AnnotationsListPath"})
5107    {
5108        if(not -f $AnnotationsListPath) {
5109            exitStatus("Access_Error", "can't access file \'$AnnotationsListPath\'");
5110        }
5111        foreach my $Annotation (split(/\n/, readFile($AnnotationsListPath)))
5112        {