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

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

tools update: Groovy 2.4.15, PMD 6.2.0, JAPICC 2.4

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