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

Last change on this file since 13391 was 12872, checked in by Don-vip, 3 years ago

update to JAPICC 2.3

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