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

Last change on this file since 11298 was 10845, checked in by Don-vip, 8 years ago

see #13232 - use JAPICC to generate a compatibility report between optimized jar and normal one

File size: 310.9 KB
Line 
1#!/usr/bin/perl
2###########################################################################
3# Java API Compliance Checker (JAPICC) 1.8
4# A tool for checking backward compatibility of a Java library API
5#
6# Written by Andrey Ponomarenko
7#
8# Copyright (C) 2011 Institute for System Programming, RAS
9# Copyright (C) 2011-2016 Andrey Ponomarenko's ABI Laboratory
10#
11# PLATFORMS
12# =========
13# Linux, FreeBSD, Mac OS X, MS Windows
14#
15# REQUIREMENTS
16# ============
17# Linux, FreeBSD, Mac OS X
18# - JDK or OpenJDK - development files (javap, javac)
19# - Perl 5 (5.8 or newer)
20#
21# MS Windows
22# - JDK or OpenJDK (javap, javac)
23# - Active Perl 5 (5.8 or newer)
24#
25# This program is free software: you can redistribute it and/or modify
26# it under the terms of the GNU General Public License or the GNU Lesser
27# General Public License as published by the Free Software Foundation.
28#
29# This program 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
32# GNU General Public License for more details.
33#
34# You should have received a copy of the GNU General Public License
35# and the GNU Lesser General Public License along with this program.
36# If not, see <http://www.gnu.org/licenses/>.
37###########################################################################
38use Getopt::Long;
39Getopt::Long::Configure ("posix_default", "no_ignore_case", "permute");
40use File::Path qw(mkpath rmtree);
41use File::Temp qw(tempdir);
42use File::Copy qw(copy);
43use File::Spec::Functions qw(abs2rel);
44use Cwd qw(abs_path cwd);
45use Data::Dumper;
46use Digest::MD5 qw(md5_hex);
47use Config;
48
49my $TOOL_VERSION = "1.8";
50my $API_DUMP_VERSION = "2.0";
51my $API_DUMP_MAJOR = majorVersion($API_DUMP_VERSION);
52
53my ($Help, $ShowVersion, %Descriptor, $TargetLibraryName, $CheckSeparately,
54$TestSystem, $DumpAPI, $ClassListPath, $ClientPath, $StrictCompat,
55$DumpVersion, $BinaryOnly, $TargetTitle, %TargetVersion, $SourceOnly,
56$ShortMode, $KeepInternal, $OutputReportPath, $BinaryReportPath,
57$SourceReportPath, $Debug, $Quick, $SortDump, $SkipDeprecated, $SkipClassesList,
58$ShowAccess, $AffectLimit, $JdkPath, $SkipInternalPackages, $HideTemplates,
59$HidePackages, $ShowPackages, $Minimal, $AnnotationsListPath,
60$SkipPackagesList, $OutputDumpPath, $AllAffected, $Compact,
61$SkipAnnotationsListPath, $ExternCss, $ExternJs, $SkipInternalTypes,
62$AddedAnnotations, $RemovedAnnotations, $CountMethods, %DepDump, $OldStyle);
63
64my $CmdName = get_filename($0);
65my $OSgroup = get_OSgroup();
66my $ORIG_DIR = cwd();
67my $TMP_DIR = tempdir(CLEANUP=>1);
68my $ARG_MAX = get_ARG_MAX();
69my $REPRODUCIBLE = 1;
70my $MD5_LEN = 8;
71
72my %OS_Archive = (
73 "windows"=>"zip",
74 "default"=>"tar.gz"
75);
76
77my %ERROR_CODE = (
78 # Compatible verdict
79 "Compatible"=>0,
80 "Success"=>0,
81 # Incompatible verdict
82 "Incompatible"=>1,
83 # Undifferentiated error code
84 "Error"=>2,
85 # System command is not found
86 "Not_Found"=>3,
87 # Cannot access input files
88 "Access_Error"=>4,
89 # Invalid input API dump
90 "Invalid_Dump"=>7,
91 # Incompatible version of API dump
92 "Dump_Version"=>8,
93 # Cannot find a module
94 "Module_Error"=>9
95);
96
97my %HomePage = (
98 "Dev"=>"https://github.com/lvc/japi-compliance-checker",
99 "Wiki"=>"http://ispras.linuxbase.org/index.php/Java_API_Compliance_Checker"
100);
101
102my $ShortUsage = "Java API Compliance Checker (JAPICC) $TOOL_VERSION
103A tool for checking backward compatibility of a Java library API
104Copyright (C) 2016 Andrey Ponomarenko's ABI Laboratory
105License: GNU LGPL or GNU GPL
106
107Usage: $CmdName [options]
108Example: $CmdName OLD.jar NEW.jar
109
110More info: $CmdName --help";
111
112if($#ARGV==-1)
113{
114 printMsg("INFO", $ShortUsage);
115 exit(0);
116}
117
118GetOptions("h|help!" => \$Help,
119 "v|version!" => \$ShowVersion,
120 "dumpversion!" => \$DumpVersion,
121# general options
122 "l|lib|library=s" => \$TargetLibraryName,
123 "d1|old|o=s" => \$Descriptor{1}{"Path"},
124 "d2|new|n=s" => \$Descriptor{2}{"Path"},
125# extra options
126 "client|app=s" => \$ClientPath,
127 "binary|bin!" => \$BinaryOnly,
128 "source|src!" => \$SourceOnly,
129 "v1|version1|vnum=s" => \$TargetVersion{1},
130 "v2|version2=s" => \$TargetVersion{2},
131 "s|strict!" => \$StrictCompat,
132 "keep-internal!" => \$KeepInternal,
133 "skip-internal-packages|skip-internal=s" => \$SkipInternalPackages,
134 "skip-internal-types=s" => \$SkipInternalTypes,
135 "dump|dump-api=s" => \$DumpAPI,
136 "classes-list=s" => \$ClassListPath,
137 "annotations-list=s" => \$AnnotationsListPath,
138 "skip-annotations-list=s" => \$SkipAnnotationsListPath,
139 "skip-deprecated!" => \$SkipDeprecated,
140 "skip-classes=s" => \$SkipClassesList,
141 "skip-packages=s" => \$SkipPackagesList,
142 "short" => \$ShortMode,
143 "dump-path=s" => \$OutputDumpPath,
144 "report-path=s" => \$OutputReportPath,
145 "bin-report-path=s" => \$BinaryReportPath,
146 "src-report-path=s" => \$SourceReportPath,
147 "quick!" => \$Quick,
148 "sort!" => \$SortDump,
149 "show-access!" => \$ShowAccess,
150 "limit-affected=s" => \$AffectLimit,
151 "hide-templates!" => \$HideTemplates,
152 "show-packages!" => \$ShowPackages,
153 "compact!" => \$Compact,
154 "added-annotations!" => \$AddedAnnotations,
155 "removed-annotations!" => \$RemovedAnnotations,
156 "count-methods=s" => \$CountMethods,
157 "dep1=s" => \$DepDump{1},
158 "dep2=s" => \$DepDump{2},
159 "old-style!" => \$OldStyle,
160# other options
161 "test!" => \$TestSystem,
162 "debug!" => \$Debug,
163 "title=s" => \$TargetTitle,
164 "jdk-path=s" => \$JdkPath,
165 "external-css=s" => \$ExternCss,
166 "external-js=s" => \$ExternJs,
167# deprecated
168 "minimal!" => \$Minimal,
169 "hide-packages!" => \$HidePackages,
170# private
171 "all-affected!" => \$AllAffected
172) or ERR_MESSAGE();
173
174if(@ARGV)
175{
176 if($#ARGV==1)
177 { # japi-compliance-checker OLD.jar NEW.jar
178 $Descriptor{1}{"Path"} = $ARGV[0];
179 $Descriptor{2}{"Path"} = $ARGV[1];
180 }
181 else {
182 ERR_MESSAGE();
183 }
184}
185
186sub ERR_MESSAGE()
187{
188 printMsg("INFO", "\n".$ShortUsage);
189 exit($ERROR_CODE{"Error"});
190}
191
192my $AR_EXT = getAR_EXT($OSgroup);
193
194my $HelpMessage="
195NAME:
196 Java API Compliance Checker ($CmdName)
197 Check backward compatibility of a Java library API
198
199DESCRIPTION:
200 Java API Compliance Checker (JAPICC) is a tool for checking backward
201 binary/source compatibility of a Java library API. The tool checks classes
202 declarations of old and new versions and analyzes changes that may break
203 compatibility: removed class members, added abstract methods, etc. Breakage
204 of the binary compatibility may result in crashing or incorrect behavior of
205 existing clients built with an old version of a library if they run with a
206 new one. Breakage of the source compatibility may result in recompilation
207 errors with a new library version.
208
209 JAPICC is intended for library developers and operating system maintainers
210 who are interested in ensuring backward compatibility (i.e. allow old clients
211 to run or to be recompiled with a new version of a library).
212
213 This tool is free software: you can redistribute it and/or modify it
214 under the terms of the GNU LGPL or GNU GPL.
215
216USAGE:
217 $CmdName [options]
218
219EXAMPLE:
220 $CmdName OLD.jar NEW.jar
221 OR
222 $CmdName -lib NAME -old OLD.xml -new NEW.xml
223 OLD.xml and NEW.xml are XML-descriptors:
224
225 <version>
226 1.0
227 </version>
228
229 <archives>
230 /path1/to/JAR(s)/
231 /path2/to/JAR(s)/
232 ...
233 </archives>
234
235INFORMATION OPTIONS:
236 -h|-help
237 Print this help.
238
239 -v|-version
240 Print version information.
241
242 -dumpversion
243 Print the tool version ($TOOL_VERSION) and don't do anything else.
244
245GENERAL OPTIONS:
246 -l|-lib|-library NAME
247 Library name (without version).
248
249 -d1|-old|-o PATH
250 Descriptor of 1st (old) library version.
251 It may be one of the following:
252
253 1. Java ARchive (*.jar)
254 2. XML-descriptor (VERSION.xml file):
255
256 <version>
257 1.0
258 </version>
259
260 <archives>
261 /path1/to/JAR(s)/
262 /path2/to/JAR(s)/
263 ...
264 </archives>
265
266 ...
267
268 3. API dump generated by -dump option
269
270 If you are using 1, 4-6 descriptor types then you should
271 specify version numbers with -v1 and -v2 options too.
272
273 If you are using *.jar as a descriptor then the tool will try to
274 get implementation version from MANIFEST.MF file.
275
276 -d2|-new|-n PATH
277 Descriptor of 2nd (new) library version.
278
279EXTRA OPTIONS:
280 -client|-app PATH
281 This option allows to specify the client Java ARchive that should be
282 checked for portability to the new library version.
283
284 -binary|-bin
285 Show \"Binary\" compatibility problems only.
286 Generate report to \"bin_compat_report.html\".
287
288 -source|-src
289 Show \"Source\" compatibility problems only.
290 Generate report to \"src_compat_report.html\".
291
292 -v1|-version1 NUM
293 Specify 1st API version outside the descriptor. This option is needed
294 if you have prefered an alternative descriptor type (see -d1 option).
295
296 In general case you should specify it in the XML descriptor:
297 <version>
298 VERSION
299 </version>
300
301 -v2|-version2 NUM
302 Specify 2nd library version outside the descriptor.
303
304 -vnum NUM
305 Specify the library version in the generated API dump.
306
307 -s|-strict
308 Treat all API compatibility warnings as problems.
309
310 -keep-internal
311 Do not skip checking of these packages:
312 *impl*
313 *internal*
314 *examples*
315
316 -skip-internal-packages PATTERN
317 Do not check packages matched by the pattern.
318
319 -skip-internal-types PATTERN
320 Do not check types (classes and interfaces) matched by the pattern.
321
322 -dump|-dump-api PATH
323 Dump library API to gzipped TXT format file. You can transfer it
324 anywhere and pass instead of the descriptor. Also it may be used
325 for debugging the tool. Compatible dump versions: $API_DUMP_MAJOR.0<=V<=$API_DUMP_VERSION
326
327 -classes-list PATH
328 This option allows to specify a file with a list
329 of classes that should be checked, other classes will not be checked.
330
331 -annotations-list PATH
332 Specifies a file with a list of annotations. The tool will check only
333 classes annotated by the annotations from the list. Other classes
334 will not be checked.
335
336 -skip-annotations-list PATH
337 Skip checking of classes annotated by the annotations in the list.
338
339 -skip-deprecated
340 Skip analysis of deprecated methods and classes.
341
342 -skip-classes PATH
343 This option allows to specify a file with a list
344 of classes that should not be checked.
345
346 -skip-packages PATH
347 This option allows to specify a file with a list
348 of packages that should not be checked.
349
350 -short
351 Do not list added/removed methods.
352
353 -dump-path PATH
354 Specify a *.api.$AR_EXT or *.api file path where to generate an API dump.
355 Default:
356 abi_dumps/LIB_NAME/LIB_NAME_VERSION.api.$AR_EXT
357
358 -report-path PATH
359 Path to compatibility report.
360 Default:
361 compat_reports/LIB_NAME/V1_to_V2/compat_report.html
362
363 -bin-report-path PATH
364 Path to \"Binary\" compatibility report.
365 Default:
366 compat_reports/LIB_NAME/V1_to_V2/bin_compat_report.html
367
368 -src-report-path PATH
369 Path to \"Source\" compatibility report.
370 Default:
371 compat_reports/LIB_NAME/V1_to_V2/src_compat_report.html
372
373 -quick
374 Quick analysis.
375 Disabled:
376 - analysis of method parameter names
377 - analysis of class field values
378 - analysis of usage of added abstract methods
379 - distinction of deprecated methods and classes
380
381 -sort
382 Enable sorting of data in API dumps.
383
384 -show-access
385 Show access level of non-public methods listed in the report.
386
387 -hide-templates
388 Hide template parameters in the report.
389
390 -hide-packages
391 -minimal
392 Do nothing.
393
394 -show-packages
395 Show package names in the report.
396
397 -limit-affected LIMIT
398 The maximum number of affected methods listed under the description
399 of the changed type in the report.
400
401 -compact
402 Try to simplify formatting and reduce size of the report (for a big
403 set of changes).
404
405 -added-annotations
406 Apply filters by annotations only to new version of the library.
407
408 -removed-annotations
409 Apply filters by annotations only to old version of the library.
410
411 -count-methods PATH
412 Count total public methods in the API dump.
413
414 -dep1 PATH
415 -dep2 PATH
416 Path to the API dump of the required dependency archive. It will
417 be used to resolve overwritten methods and more.
418
419 -old-style
420 Generate old-style report.
421
422OTHER OPTIONS:
423 -test
424 Run internal tests. Create two incompatible versions of a sample library
425 and run the tool to check them for compatibility. This option allows to
426 check if the tool works correctly in the current environment.
427
428 -debug
429 Debugging mode. Print debug info on the screen. Save intermediate
430 analysis stages in the debug directory:
431 debug/LIB_NAME/VER/
432
433 Also consider using -dump option for debugging the tool.
434
435 -title NAME
436 Change library name in the report title to NAME. By default
437 will be displayed a name specified by -l option.
438
439 -jdk-path PATH
440 Path to the JDK install tree (e.g. /usr/lib/jvm/java-7-openjdk-amd64).
441
442 -external-css PATH
443 Generate CSS styles file to PATH. This helps to save space when
444 generating thousands of reports.
445
446 -external-js PATH
447 Generate JS script file to PATH.
448
449REPORT:
450 Compatibility report will be generated to:
451 compat_reports/LIB_NAME/V1_to_V2/compat_report.html
452
453EXIT CODES:
454 0 - Compatible. The tool has run without any errors.
455 non-zero - Incompatible or the tool has run with errors.
456
457MORE INFORMATION:
458 ".$HomePage{"Wiki"}."
459 ".$HomePage{"Dev"}."\n\n";
460
461sub HELP_MESSAGE()
462{ # -help
463 printMsg("INFO", $HelpMessage."\n");
464}
465
466my %TypeProblems_Kind=(
467 "Binary"=>{
468 "NonAbstract_Class_Added_Abstract_Method"=>"High",
469 "Abstract_Class_Added_Abstract_Method"=>"Safe",
470 "Abstract_Class_Added_Abstract_Method_Invoked_By_Others"=>"Medium",
471 "Class_Removed_Abstract_Method"=>"High",
472 "Interface_Added_Abstract_Method"=>"Safe",
473 "Interface_Added_Abstract_Method_Invoked_By_Others"=>"Medium",
474 "Interface_Removed_Abstract_Method"=>"High",
475 "Removed_Class"=>"High",
476 "Removed_Interface"=>"High",
477 "Class_Method_Became_Abstract"=>"High",
478 "Class_Method_Became_NonAbstract"=>"Low",
479 "Interface_Method_Became_NonDefault"=>"High",
480 "Interface_Method_Became_Default"=>"Safe",
481 "Added_Super_Class"=>"Low",
482 "Abstract_Class_Added_Super_Abstract_Class"=>"Safe",
483 "Abstract_Class_Added_Super_Abstract_Class_Invoked_By_Others"=>"Medium",
484 "Removed_Super_Class"=>"Medium",
485 "Changed_Super_Class"=>"Medium",
486 "Abstract_Class_Added_Super_Interface"=>"Safe",
487 "Abstract_Class_Added_Super_Interface_Invoked_By_Others"=>"Medium",
488 "Abstract_Class_Added_Super_Interface_With_Implemented_Methods"=>"Safe",
489 "Class_Removed_Super_Interface"=>"High",
490 "Interface_Added_Super_Interface"=>"Safe",
491 "Interface_Added_Super_Interface_Used_By_Others"=>"Medium",
492 "Interface_Added_Super_Constant_Interface"=>"Low",
493 "Interface_Added_Super_Interface_With_Implemented_Methods"=>"Safe",
494 "Interface_Removed_Super_Interface"=>"High",
495 "Interface_Removed_Super_Constant_Interface"=>"Safe",
496 "Class_Became_Interface"=>"High",
497 "Interface_Became_Class"=>"High",
498 "Class_Became_Final"=>"High",
499 "Class_Became_Abstract"=>"High",
500 "Class_Added_Field"=>"Safe",
501 "Interface_Added_Field"=>"Safe",
502 "Removed_NonConstant_Field"=>"High",
503 "Removed_Constant_Field"=>"Low",
504 "Renamed_Field"=>"High",
505 "Renamed_Constant_Field"=>"Low",
506 "Changed_Field_Type"=>"High",
507 "Changed_Field_Access"=>"High",
508 "Changed_Final_Field_Value"=>"Medium",
509 "Changed_Final_Version_Field_Value"=>"Low",
510 "Field_Became_Final"=>"Medium",
511 "Field_Became_NonFinal"=>"Low",
512 "NonConstant_Field_Became_Static"=>"High",
513 "NonConstant_Field_Became_NonStatic"=>"High",
514 "Class_Overridden_Method"=>"Low",
515 "Class_Method_Moved_Up_Hierarchy"=>"Low"
516 },
517 "Source"=>{
518 "NonAbstract_Class_Added_Abstract_Method"=>"High",
519 "Abstract_Class_Added_Abstract_Method"=>"High",
520 "Abstract_Class_Added_Abstract_Method_Invoked_By_Others"=>"High",
521 "Interface_Added_Abstract_Method"=>"High",
522 "Interface_Added_Abstract_Method_Invoked_By_Others"=>"High",
523 "Class_Removed_Abstract_Method"=>"High",
524 "Interface_Removed_Abstract_Method"=>"High",
525 "Removed_Class"=>"High",
526 "Removed_Interface"=>"High",
527 "Class_Method_Became_Abstract"=>"High",
528 "Class_Method_Became_NonAbstract"=>"Safe",
529 "Interface_Method_Became_NonDefault"=>"High",
530 "Interface_Method_Became_Default"=>"Safe",
531 "Added_Super_Class"=>"Low",
532 "Abstract_Class_Added_Super_Abstract_Class"=>"High",
533 "Abstract_Class_Added_Super_Abstract_Class_Invoked_By_Others"=>"High",
534 "Removed_Super_Class"=>"Medium",
535 "Changed_Super_Class"=>"Medium",
536 "Abstract_Class_Added_Super_Interface"=>"High",
537 "Abstract_Class_Added_Super_Interface_Invoked_By_Others"=>"High",
538 "Abstract_Class_Added_Super_Interface_With_Implemented_Methods"=>"Safe",
539 "Class_Removed_Super_Interface"=>"High",
540 "Interface_Added_Super_Interface"=>"High",
541 "Interface_Added_Super_Interface_Used_By_Others"=>"High",
542 "Interface_Added_Super_Constant_Interface"=>"Low",
543 "Interface_Added_Super_Interface_With_Implemented_Methods"=>"Safe",
544 "Interface_Removed_Super_Interface"=>"High",
545 "Interface_Removed_Super_Constant_Interface"=>"High",
546 "Class_Became_Interface"=>"High",
547 "Interface_Became_Class"=>"High",
548 "Class_Became_Final"=>"High",
549 "Class_Became_Abstract"=>"High",
550 "Class_Added_Field"=>"Safe",
551 "Interface_Added_Field"=>"Safe",
552 "Removed_NonConstant_Field"=>"High",
553 "Removed_Constant_Field"=>"High",
554 "Renamed_Field"=>"High",
555 "Renamed_Constant_Field"=>"High",
556 "Changed_Field_Type"=>"High",
557 "Changed_Field_Access"=>"High",
558 "Field_Became_Final"=>"Medium",
559 "Constant_Field_Became_NonStatic"=>"High",
560 "NonConstant_Field_Became_NonStatic"=>"High",
561 "Removed_Annotation"=>"High"
562 }
563);
564
565my %MethodProblems_Kind=(
566 "Binary"=>{
567 "Added_Method"=>"Safe",
568 "Removed_Method"=>"High",
569 "Method_Became_Static"=>"High",
570 "Method_Became_NonStatic"=>"High",
571 "NonStatic_Method_Became_Final"=>"Medium",
572 "Changed_Method_Access"=>"High",
573 "Method_Became_Synchronized"=>"Low",
574 "Method_Became_NonSynchronized"=>"Low",
575 "Method_Became_Abstract"=>"High",
576 "Method_Became_NonAbstract"=>"Low",
577 "Method_Became_NonDefault"=>"High",
578 "Method_Became_Default"=>"Safe",
579 "NonAbstract_Method_Added_Checked_Exception"=>"Low",
580 "NonAbstract_Method_Removed_Checked_Exception"=>"Low",
581 "Added_Unchecked_Exception"=>"Low",
582 "Removed_Unchecked_Exception"=>"Low",
583 "Variable_Arity_To_Array"=>"Low",# not implemented yet
584 "Changed_Method_Return_From_Void"=>"High"
585 },
586 "Source"=>{
587 "Added_Method"=>"Safe",
588 "Removed_Method"=>"High",
589 "Method_Became_Static"=>"Low",
590 "Method_Became_NonStatic"=>"High",
591 "Static_Method_Became_Final"=>"Medium",
592 "NonStatic_Method_Became_Final"=>"Medium",
593 "Changed_Method_Access"=>"High",
594 "Method_Became_Abstract"=>"High",
595 "Method_Became_NonAbstract"=>"Safe",
596 "Method_Became_NonDefault"=>"High",
597 "Method_Became_Default"=>"Safe",
598 "Abstract_Method_Added_Checked_Exception"=>"Medium",
599 "NonAbstract_Method_Added_Checked_Exception"=>"Medium",
600 "Abstract_Method_Removed_Checked_Exception"=>"Medium",
601 "NonAbstract_Method_Removed_Checked_Exception"=>"Medium"
602 }
603);
604
605my %KnownRuntimeExceptions= map {$_=>1} (
606# To separate checked- and unchecked- exceptions
607 "java.lang.AnnotationTypeMismatchException",
608 "java.lang.ArithmeticException",
609 "java.lang.ArrayStoreException",
610 "java.lang.BufferOverflowException",
611 "java.lang.BufferUnderflowException",
612 "java.lang.CannotRedoException",
613 "java.lang.CannotUndoException",
614 "java.lang.ClassCastException",
615 "java.lang.CMMException",
616 "java.lang.ConcurrentModificationException",
617 "java.lang.DataBindingException",
618 "java.lang.DOMException",
619 "java.lang.EmptyStackException",
620 "java.lang.EnumConstantNotPresentException",
621 "java.lang.EventException",
622 "java.lang.IllegalArgumentException",
623 "java.lang.IllegalMonitorStateException",
624 "java.lang.IllegalPathStateException",
625 "java.lang.IllegalStateException",
626 "java.lang.ImagingOpException",
627 "java.lang.IncompleteAnnotationException",
628 "java.lang.IndexOutOfBoundsException",
629 "java.lang.JMRuntimeException",
630 "java.lang.LSException",
631 "java.lang.MalformedParameterizedTypeException",
632 "java.lang.MirroredTypeException",
633 "java.lang.MirroredTypesException",
634 "java.lang.MissingResourceException",
635 "java.lang.NegativeArraySizeException",
636 "java.lang.NoSuchElementException",
637 "java.lang.NoSuchMechanismException",
638 "java.lang.NullPointerException",
639 "java.lang.ProfileDataException",
640 "java.lang.ProviderException",
641 "java.lang.RasterFormatException",
642 "java.lang.RejectedExecutionException",
643 "java.lang.SecurityException",
644 "java.lang.SystemException",
645 "java.lang.TypeConstraintException",
646 "java.lang.TypeNotPresentException",
647 "java.lang.UndeclaredThrowableException",
648 "java.lang.UnknownAnnotationValueException",
649 "java.lang.UnknownElementException",
650 "java.lang.UnknownEntityException",
651 "java.lang.UnknownTypeException",
652 "java.lang.UnmodifiableSetException",
653 "java.lang.UnsupportedOperationException",
654 "java.lang.WebServiceException",
655 "java.lang.WrongMethodTypeException"
656);
657
658my %Slash_Type=(
659 "default"=>"/",
660 "windows"=>"\\"
661);
662
663my $SLASH = $Slash_Type{$OSgroup}?$Slash_Type{$OSgroup}:$Slash_Type{"default"};
664
665my %OS_AddPath=(
666# this data needed if tool can't detect it automatically
667"macos"=>{
668 "bin"=>{"/Developer/usr/bin"=>1}},
669"beos"=>{
670 "bin"=>{"/boot/common/bin"=>1,"/boot/system/bin"=>1,"/boot/develop/abi"=>1}}
671);
672
673#Global variables
674my %RESULT;
675my $ExtractCounter = 0;
676my %Cache;
677my $TOP_REF = "<a class='top_ref' href='#Top'>to the top</a>";
678my %DEBUG_PATH;
679
680#Types
681my %TypeInfo;
682my $TYPE_ID = 0;
683my %CheckedTypes;
684my %TName_Tid;
685my %Class_Constructed;
686
687#Classes
688my %ClassList_User;
689my %UsedMethods_Client;
690my %UsedFields_Client;
691my %UsedClasses_Client;
692my %LibArchives;
693my %Class_Methods;
694my %Class_AbstractMethods;
695my %Class_Fields;
696my %MethodUsed;
697my %ClassMethod_AddedUsed;
698# my %FieldUsed;
699
700#java.lang.Object
701my %JavaObjectMethod = (
702
703 "java/lang/Object.clone:()Ljava/lang/Object;" => 1,
704 "java/lang/Object.equals:(Ljava/lang/Object;)Z" => 1,
705 "java/lang/Object.finalize:()V" => 1,
706 "java/lang/Object.getClass:()Ljava/lang/Class;" => 1,
707 "java/lang/Object.hashCode:()I" => 1,
708 "java/lang/Object.notify:()V" => 1,
709 "java/lang/Object.notifyAll:()V" => 1,
710 "java/lang/Object.toString:()Ljava/lang/String;" => 1,
711 "java/lang/Object.wait:()V" => 1,
712 "java/lang/Object.wait:(J)V" => 1,
713 "java/lang/Object.wait:(JI)V" => 1
714);
715
716#Annotations
717my %AnnotationList_User;
718my %SkipAnnotationList_User;
719
720#Methods
721my %CheckedMethods;
722my %tr_name;
723
724#Merging
725my %MethodInfo;
726my $Version;
727my %AddedMethod_Abstract;
728my %RemovedMethod_Abstract;
729my %ChangedReturnFromVoid;
730my %SkipClasses;
731my %SkipPackages;
732my %KeepPackages;
733my %SkippedPackage;
734
735#Report
736my %TypeChanges;
737
738#Recursion locks
739my @RecurSymlink;
740my @RecurTypes;
741
742#System
743my %SystemPaths;
744my %DefaultBinPaths;
745
746#Problem descriptions
747my %CompatProblems;
748my %TotalAffected;
749
750#Speedup
751my %TypeProblemsIndex;
752
753#Rerort
754my $ContentID = 1;
755my $ContentSpanStart = "<span class=\"section\" onclick=\"sC(this, 'CONTENT_ID')\">\n";
756my $ContentSpanStart_Affected = "<span class=\"sect_aff\" onclick=\"sC(this, 'CONTENT_ID')\">\n";
757my $ContentSpanEnd = "</span>\n";
758my $ContentDivStart = "<div id=\"CONTENT_ID\" style=\"display:none;\">\n";
759my $ContentDivEnd = "</div>\n";
760my $Content_Counter = 0;
761
762#Modes
763my $JoinReport = 1;
764my $DoubleReport = 0;
765
766sub get_CmdPath($)
767{
768 my $Name = $_[0];
769 return "" if(not $Name);
770 if(defined $Cache{"get_CmdPath"}{$Name}) {
771 return $Cache{"get_CmdPath"}{$Name};
772 }
773 my $Path = search_Cmd($Name);
774 if(not $Path and $OSgroup eq "windows")
775 { # search for *.exe file
776 $Path=search_Cmd($Name.".exe");
777 }
778 if (not $Path) {
779 $Path=search_Cmd_Path($Name);
780 }
781 if($Path=~/\s/) {
782 $Path = "\"".$Path."\"";
783 }
784 return ($Cache{"get_CmdPath"}{$Name} = $Path);
785}
786
787sub search_Cmd($)
788{
789 my $Name = $_[0];
790 return "" if(not $Name);
791 if(defined $Cache{"search_Cmd"}{$Name}) {
792 return $Cache{"search_Cmd"}{$Name};
793 }
794 if(defined $JdkPath)
795 {
796 if(-x $JdkPath."/".$Name) {
797 return ($Cache{"search_Cmd"}{$Name} = $JdkPath."/".$Name);
798 }
799
800 if(-x $JdkPath."/bin/".$Name) {
801 return ($Cache{"search_Cmd"}{$Name} = $JdkPath."/bin/".$Name);
802 }
803 }
804 if(my $DefaultPath = get_CmdPath_Default($Name)) {
805 return ($Cache{"search_Cmd"}{$Name} = $DefaultPath);
806 }
807 return ($Cache{"search_Cmd"}{$Name} = "");
808}
809
810sub search_Cmd_Path($)
811{
812 my $Name = $_[0];
813 return "" if(not $Name);
814
815 if(defined $Cache{"search_Cmd_Path"}{$Name}) {
816 return $Cache{"search_Cmd_Path"}{$Name};
817 }
818
819 if(defined $SystemPaths{"bin"})
820 {
821 foreach my $Path (sort {length($a)<=>length($b)} keys(%{$SystemPaths{"bin"}}))
822 {
823 if(-f $Path."/".$Name or -f $Path."/".$Name.".exe") {
824 return ($Cache{"search_Cmd_Path"}{$Name} = joinPath($Path,$Name));
825 }
826 }
827 }
828
829 return ($Cache{"search_Cmd_Path"}{$Name} = "");
830}
831
832sub get_CmdPath_Default($)
833{ # search in PATH
834 return "" if(not $_[0]);
835 if(defined $Cache{"get_CmdPath_Default"}{$_[0]}) {
836 return $Cache{"get_CmdPath_Default"}{$_[0]};
837 }
838 return ($Cache{"get_CmdPath_Default"}{$_[0]} = get_CmdPath_Default_I($_[0]));
839}
840
841sub get_CmdPath_Default_I($)
842{ # search in PATH
843 my $Name = $_[0];
844 if($Name=~/find/)
845 { # special case: search for "find" utility
846 if(`find \"$TMP_DIR\" -maxdepth 0 2>\"$TMP_DIR/null\"`) {
847 return "find";
848 }
849 }
850 if(get_version($Name)) {
851 return $Name;
852 }
853 if($OSgroup eq "windows")
854 {
855 if(`$Name /? 2>\"$TMP_DIR/null\"`) {
856 return $Name;
857 }
858 }
859 if($Name!~/which/)
860 {
861 if(my $WhichCmd = get_CmdPath("which"))
862 {
863 if(`$WhichCmd $Name 2>\"$TMP_DIR/null\"`) {
864 return $Name;
865 }
866 }
867 }
868 foreach my $Path (sort {length($a)<=>length($b)} keys(%DefaultBinPaths))
869 {
870 if(-f $Path."/".$Name) {
871 return joinPath($Path,$Name);
872 }
873 }
874 return "";
875}
876
877sub showPos($)
878{
879 my $Number = $_[0];
880 if(not $Number) {
881 $Number = 1;
882 }
883 else {
884 $Number = int($Number)+1;
885 }
886 if($Number>3) {
887 return $Number."th";
888 }
889 elsif($Number==1) {
890 return "1st";
891 }
892 elsif($Number==2) {
893 return "2nd";
894 }
895 elsif($Number==3) {
896 return "3rd";
897 }
898 else {
899 return $Number;
900 }
901}
902
903sub getAR_EXT($)
904{
905 my $Target = $_[0];
906 if(my $Ext = $OS_Archive{$Target}) {
907 return $Ext;
908 }
909 return $OS_Archive{"default"};
910}
911
912sub readDescriptor($$)
913{
914 my ($LibVersion, $Content) = @_;
915 return if(not $LibVersion);
916 my $DName = $DumpAPI?"descriptor":"descriptor \"d$LibVersion\"";
917 if(not $Content) {
918 exitStatus("Error", "$DName is empty");
919 }
920 if($Content!~/\</) {
921 exitStatus("Error", "descriptor should be one of the following:\n Java ARchive, XML descriptor, gzipped API dump or directory with Java ARchives.");
922 }
923 $Content=~s/\/\*(.|\n)+?\*\///g;
924 $Content=~s/<\!--(.|\n)+?-->//g;
925 $Descriptor{$LibVersion}{"Version"} = parseTag(\$Content, "version");
926 $Descriptor{$LibVersion}{"Version"} = $TargetVersion{$LibVersion} if($TargetVersion{$LibVersion});
927 if($Descriptor{$LibVersion}{"Version"} eq "") {
928 exitStatus("Error", "version in the $DName is not specified (<version> section)");
929 }
930
931 my $DArchives = parseTag(\$Content, "archives");
932 if(not $DArchives){
933 exitStatus("Error", "Java ARchives in the $DName are not specified (<archive> section)");
934 }
935 else
936 {# append the descriptor Java ARchives list
937 if($Descriptor{$LibVersion}{"Archives"}) {
938 $Descriptor{$LibVersion}{"Archives"} .= "\n".$DArchives;
939 }
940 else {
941 $Descriptor{$LibVersion}{"Archives"} = $DArchives;
942 }
943 foreach my $Path (split(/\s*\n\s*/, $DArchives))
944 {
945 if(not -e $Path) {
946 exitStatus("Access_Error", "can't access \'$Path\'");
947 }
948 }
949 }
950 foreach my $Package (split(/\s*\n\s*/, parseTag(\$Content, "skip_packages"))) {
951 $SkipPackages{$LibVersion}{$Package} = 1;
952 }
953 foreach my $Package (split(/\s*\n\s*/, parseTag(\$Content, "packages"))) {
954 $KeepPackages{$LibVersion}{$Package} = 1;
955 }
956}
957
958sub parseTag($$)
959{
960 my ($CodeRef, $Tag) = @_;
961 return "" if(not $CodeRef or not ${$CodeRef} or not $Tag);
962 if(${$CodeRef}=~s/\<\Q$Tag\E\>((.|\n)+?)\<\/\Q$Tag\E\>//)
963 {
964 my $Content = $1;
965 $Content=~s/(\A\s+|\s+\Z)//g;
966 return $Content;
967 }
968 else {
969 return "";
970 }
971}
972
973sub ignore_path($$)
974{
975 my ($Path, $Prefix) = @_;
976 return 1 if(not $Path or not -e $Path
977 or not $Prefix or not -e $Prefix);
978 return 1 if($Path=~/\~\Z/);# skipping system backup files
979 # skipping hidden .svn, .git, .bzr, .hg and CVS directories
980 return 1 if(cut_path_prefix($Path, $Prefix)=~/(\A|[\/\\]+)(\.(svn|git|bzr|hg)|CVS)([\/\\]+|\Z)/);
981 return 0;
982}
983
984sub cut_path_prefix($$)
985{
986 my ($Path, $Prefix) = @_;
987 $Prefix=~s/[\/\\]+\Z//;
988 $Path=~s/\A\Q$Prefix\E([\/\\]+|\Z)//;
989 return $Path;
990}
991
992sub get_filename($)
993{ # much faster than basename() from File::Basename module
994 if(defined $Cache{"get_filename"}{$_[0]}) {
995 return $Cache{"get_filename"}{$_[0]};
996 }
997 if($_[0] and $_[0]=~/([^\/\\]+)[\/\\]*\Z/) {
998 return ($Cache{"get_filename"}{$_[0]}=$1);
999 }
1000 return ($Cache{"get_filename"}{$_[0]}="");
1001}
1002
1003sub get_dirname($)
1004{ # much faster than dirname() from File::Basename module
1005 if(defined $Cache{"get_dirname"}{$_[0]}) {
1006 return $Cache{"get_dirname"}{$_[0]};
1007 }
1008 if($_[0] and $_[0]=~/\A(.*?)[\/\\]+[^\/\\]*[\/\\]*\Z/) {
1009 return ($Cache{"get_dirname"}{$_[0]}=$1);
1010 }
1011 return ($Cache{"get_dirname"}{$_[0]}="");
1012}
1013
1014sub separate_path($) {
1015 return (get_dirname($_[0]), get_filename($_[0]));
1016}
1017
1018sub joinPath($$)
1019{
1020 return join($SLASH, @_);
1021}
1022
1023sub get_abs_path($)
1024{ # abs_path() should NOT be called for absolute inputs
1025 # because it can change them
1026 my $Path = $_[0];
1027 if(not is_abs($Path)) {
1028 $Path = abs_path($Path);
1029 }
1030 return $Path;
1031}
1032
1033sub is_abs($) {
1034 return ($_[0]=~/\A(\/|\w+:[\/\\])/);
1035}
1036
1037sub cmd_find($$$$)
1038{
1039 my ($Path, $Type, $Name, $MaxDepth) = @_;
1040 return () if(not $Path or not -e $Path);
1041 if($OSgroup eq "windows")
1042 {
1043 my $DirCmd = get_CmdPath("dir");
1044 if(not $DirCmd) {
1045 exitStatus("Not_Found", "can't find \"dir\" command");
1046 }
1047 $Path=~s/[\\]+\Z//;
1048 $Path = get_abs_path($Path);
1049 my $Cmd = $DirCmd." \"$Path\" /B /O";
1050 if($MaxDepth!=1) {
1051 $Cmd .= " /S";
1052 }
1053 if($Type eq "d") {
1054 $Cmd .= " /AD";
1055 }
1056 my @Files = ();
1057 if($Name)
1058 { # FIXME: how to search file names in MS shell?
1059 $Name=~s/\*/.*/g if($Name!~/\]/);
1060 foreach my $File (split(/\n/, `$Cmd`))
1061 {
1062 if($File=~/$Name\Z/i) {
1063 push(@Files, $File);
1064 }
1065 }
1066 }
1067 else {
1068 @Files = split(/\n/, `$Cmd 2>\"$TMP_DIR/null\"`);
1069 }
1070 my @AbsPaths = ();
1071 foreach my $File (@Files)
1072 {
1073 if(not is_abs($File)) {
1074 $File = joinPath($Path, $File);
1075 }
1076 if($Type eq "f" and not -f $File)
1077 { # skip dirs
1078 next;
1079 }
1080 push(@AbsPaths, $File);
1081 }
1082 if($Type eq "d") {
1083 push(@AbsPaths, $Path);
1084 }
1085 return @AbsPaths;
1086 }
1087 else
1088 {
1089 my $FindCmd = get_CmdPath("find");
1090 if(not $FindCmd) {
1091 exitStatus("Not_Found", "can't find a \"find\" command");
1092 }
1093 $Path = get_abs_path($Path);
1094 if(-d $Path and -l $Path
1095 and $Path!~/\/\Z/)
1096 { # for directories that are symlinks
1097 $Path.="/";
1098 }
1099 my $Cmd = $FindCmd." \"$Path\"";
1100 if($MaxDepth) {
1101 $Cmd .= " -maxdepth $MaxDepth";
1102 }
1103 if($Type) {
1104 $Cmd .= " -type $Type";
1105 }
1106 if($Name)
1107 {
1108 if($Name=~/\]/) {
1109 $Cmd .= " -regex \"$Name\"";
1110 }
1111 else {
1112 $Cmd .= " -name \"$Name\"";
1113 }
1114 }
1115 return split(/\n/, `$Cmd 2>\"$TMP_DIR/null\"`);
1116 }
1117}
1118
1119sub path_format($$)
1120{ # forward slash to pass into MinGW GCC
1121 my ($Path, $Fmt) = @_;
1122 if($Fmt eq "windows")
1123 {
1124 $Path=~s/\//\\/g;
1125 $Path=lc($Path);
1126 }
1127 else {
1128 $Path=~s/\\/\//g;
1129 }
1130 return $Path;
1131}
1132
1133sub unpackDump($)
1134{
1135 my $Path = $_[0];
1136 return "" if(not $Path or not -e $Path);
1137
1138 if(isDumpFile($Path)) {
1139 return $Path;
1140 }
1141
1142 $Path = get_abs_path($Path);
1143 $Path = path_format($Path, $OSgroup);
1144 my ($Dir, $FileName) = separate_path($Path);
1145 my $UnpackDir = $TMP_DIR."/unpack";
1146 if(-d $UnpackDir) {
1147 rmtree($UnpackDir);
1148 }
1149 mkpath($UnpackDir);
1150 if($FileName=~s/\Q.zip\E\Z//g)
1151 { # *.zip
1152 my $UnzipCmd = get_CmdPath("unzip");
1153 if(not $UnzipCmd) {
1154 exitStatus("Not_Found", "can't find \"unzip\" command");
1155 }
1156 chdir($UnpackDir);
1157 system("$UnzipCmd \"$Path\" >contents.txt");
1158 if($?) {
1159 exitStatus("Error", "can't extract \'$Path\'");
1160 }
1161 chdir($ORIG_DIR);
1162 my @Contents = ();
1163 foreach (split("\n", readFile("$UnpackDir/contents.txt")))
1164 {
1165 if(/inflating:\s*([^\s]+)/) {
1166 push(@Contents, $1);
1167 }
1168 }
1169 if(not @Contents) {
1170 exitStatus("Error", "can't extract \'$Path\'");
1171 }
1172 return joinPath($UnpackDir, $Contents[0]);
1173 }
1174 elsif($FileName=~s/\Q.tar.gz\E\Z//g)
1175 { # *.tar.gz
1176 if($OSgroup eq "windows")
1177 { # -xvzf option is not implemented in tar.exe (2003)
1178 # use "gzip.exe -k -d -f" + "tar.exe -xvf" instead
1179 my $TarCmd = get_CmdPath("tar");
1180 if(not $TarCmd) {
1181 exitStatus("Not_Found", "can't find \"tar\" command");
1182 }
1183 my $GzipCmd = get_CmdPath("gzip");
1184 if(not $GzipCmd) {
1185 exitStatus("Not_Found", "can't find \"gzip\" command");
1186 }
1187 chdir($UnpackDir);
1188 qx/$GzipCmd -k -d -f "$Path"/; # keep input files (-k)
1189 if($?) {
1190 exitStatus("Error", "can't extract \'$Path\'");
1191 }
1192 my @Contents = qx/$TarCmd -xvf "$Dir\\$FileName.tar"/;
1193 if($? or not @Contents) {
1194 exitStatus("Error", "can't extract \'$Path\'");
1195 }
1196 chdir($ORIG_DIR);
1197 unlink($Dir."/".$FileName.".tar");
1198 chomp $Contents[0];
1199 return joinPath($UnpackDir, $Contents[0]);
1200 }
1201 else
1202 { # Linux, Unix, OS X
1203 my $TarCmd = get_CmdPath("tar");
1204 if(not $TarCmd) {
1205 exitStatus("Not_Found", "can't find \"tar\" command");
1206 }
1207 chdir($UnpackDir);
1208 my @Contents = qx/$TarCmd -xvzf "$Path" 2>&1/;
1209 if($? or not @Contents) {
1210 exitStatus("Error", "can't extract \'$Path\'");
1211 }
1212 chdir($ORIG_DIR);
1213 $Contents[0]=~s/^x //; # OS X
1214 chomp $Contents[0];
1215 return joinPath($UnpackDir, $Contents[0]);
1216 }
1217 }
1218}
1219
1220sub mergeClasses()
1221{
1222 my %ReportedRemoved = undef;
1223
1224 foreach my $ClassName (keys(%{$Class_Methods{1}}))
1225 {
1226 next if(not $ClassName);
1227 my $Type1_Id = $TName_Tid{1}{$ClassName};
1228 my %Type1 = get_Type($Type1_Id, 1);
1229
1230 if($Type1{"Type"}!~/class|interface/) {
1231 next;
1232 }
1233
1234 if(defined $Type1{"Access"}
1235 and $Type1{"Access"}=~/private/) {
1236 next;
1237 }
1238
1239 if(not classFilter(\%Type1, 1, 0)) {
1240 next;
1241 }
1242
1243 my $Type2_Id = $TName_Tid{2}{$ClassName};
1244 if(not $Type2_Id)
1245 { # classes and interfaces with public methods
1246 foreach my $Method (keys(%{$Class_Methods{1}{$ClassName}}))
1247 {
1248 if(not methodFilter($Method, 1)) {
1249 next;
1250 }
1251
1252 $CheckedTypes{$ClassName} = 1;
1253 $CheckedMethods{$Method} = 1;
1254
1255 if($Type1{"Type"} eq "class")
1256 {
1257 %{$CompatProblems{$Method}{"Removed_Class"}{"this"}} = (
1258 "Type_Name"=>$ClassName,
1259 "Target"=>$ClassName );
1260 }
1261 else
1262 {
1263 %{$CompatProblems{$Method}{"Removed_Interface"}{"this"}} = (
1264 "Type_Name"=>$ClassName,
1265 "Target"=>$ClassName );
1266 }
1267
1268 $ReportedRemoved{$ClassName} = 1;
1269 }
1270 }
1271 }
1272
1273 foreach my $Class_Id (keys(%{$TypeInfo{1}}))
1274 {
1275 my %Class1 = get_Type($Class_Id, 1);
1276
1277 if($Class1{"Type"}!~/class|interface/) {
1278 next;
1279 }
1280
1281 if(defined $Class1{"Access"}
1282 and $Class1{"Access"}=~/private/) {
1283 next;
1284 }
1285
1286 if(not classFilter(\%Class1, 1, 1)) {
1287 next;
1288 }
1289
1290 my $ClassName = $Class1{"Name"};
1291
1292 if(my $Class2_Id = $TName_Tid{2}{$ClassName})
1293 { # classes and interfaces with public static fields
1294 if(not defined $Class_Methods{1}{$ClassName})
1295 {
1296 my %Class2 = get_Type($Class2_Id, 2);
1297
1298 foreach my $Field (keys(%{$Class1{"Fields"}}))
1299 {
1300 my $FieldInfo = $Class1{"Fields"}{$Field};
1301
1302 my $FAccess = $FieldInfo->{"Access"};
1303 if($FAccess=~/private/) {
1304 next;
1305 }
1306
1307 if($FieldInfo->{"Static"})
1308 {
1309 $CheckedTypes{$ClassName} = 1;
1310
1311 if(not defined $Class2{"Fields"}{$Field})
1312 {
1313 %{$CompatProblems{".client_method"}{"Removed_NonConstant_Field"}{$Field}}=(
1314 "Target"=>$Field,
1315 "Type_Name"=>$ClassName,
1316 "Type_Type"=>$Class1{"Type"},
1317 "Field_Type"=>get_TypeName($FieldInfo->{"Type"}, 1) );
1318 }
1319 }
1320 }
1321 }
1322 }
1323 else
1324 { # removed
1325 if(defined $Class1{"Annotation"})
1326 {
1327 %{$CompatProblems{".client_method"}{"Removed_Annotation"}{"this"}} = (
1328 "Type_Name"=>$ClassName,
1329 "Target"=>$ClassName );
1330 }
1331
1332 if(not defined $Class_Methods{1}{$ClassName})
1333 {
1334 # classes and interfaces with public static fields
1335 if(not defined $ReportedRemoved{$ClassName})
1336 {
1337 foreach my $Field (keys(%{$Class1{"Fields"}}))
1338 {
1339 my $FieldInfo = $Class1{"Fields"}{$Field};
1340
1341 my $FAccess = $FieldInfo->{"Access"};
1342 if($FAccess=~/private/) {
1343 next;
1344 }
1345
1346 if($FieldInfo->{"Static"})
1347 {
1348 $CheckedTypes{$ClassName} = 1;
1349
1350 if($Class1{"Type"} eq "class")
1351 {
1352 %{$CompatProblems{".client_method"}{"Removed_Class"}{"this"}} = (
1353 "Type_Name"=>$ClassName,
1354 "Target"=>$ClassName );
1355 }
1356 else
1357 {
1358 %{$CompatProblems{".client_method"}{"Removed_Interface"}{"this"}} = (
1359 "Type_Name"=>$ClassName,
1360 "Target"=>$ClassName );
1361 }
1362 }
1363 }
1364 }
1365 }
1366 }
1367 }
1368}
1369
1370sub findFieldPair($$)
1371{
1372 my ($Field_Pos, $Pair_Type) = @_;
1373 foreach my $Pair_Name (sort keys(%{$Pair_Type->{"Fields"}}))
1374 {
1375 if(defined $Pair_Type->{"Fields"}{$Pair_Name})
1376 {
1377 if($Pair_Type->{"Fields"}{$Pair_Name}{"Pos"} eq $Field_Pos) {
1378 return $Pair_Name;
1379 }
1380 }
1381 }
1382 return "lost";
1383}
1384
1385my %Severity_Val=(
1386 "High"=>3,
1387 "Medium"=>2,
1388 "Low"=>1,
1389 "Safe"=>-1
1390);
1391
1392sub isRecurType($$)
1393{
1394 foreach (@RecurTypes)
1395 {
1396 if($_->{"Tid1"} eq $_[0]
1397 and $_->{"Tid2"} eq $_[1])
1398 {
1399 return 1;
1400 }
1401 }
1402 return 0;
1403}
1404
1405sub pushType($$)
1406{
1407 my %TypeDescriptor=(
1408 "Tid1" => $_[0],
1409 "Tid2" => $_[1] );
1410 push(@RecurTypes, \%TypeDescriptor);
1411}
1412
1413sub get_SFormat($)
1414{
1415 my $Name = $_[0];
1416 $Name=~s/\./\//g;
1417 return $Name;
1418}
1419
1420sub get_PFormat($)
1421{
1422 my $Name = $_[0];
1423 $Name=~s/\//./g;
1424 return $Name;
1425}
1426
1427sub get_ConstantValue($$)
1428{
1429 my ($Value, $ValueType) = @_;
1430 return "" if(not $Value);
1431 if($Value eq "\@EMPTY_STRING\@") {
1432 return "\"\"";
1433 }
1434 elsif($ValueType eq "java.lang.String") {
1435 return "\"".$Value."\"";
1436 }
1437 else {
1438 return $Value;
1439 }
1440}
1441
1442sub getInvoked($)
1443{
1444 my $TName = $_[0];
1445
1446 if(my @Invoked = sort keys(%{$ClassMethod_AddedUsed{$TName}}))
1447 {
1448 my $MFirst = $Invoked[0];
1449 my $MSignature = unmangle($MFirst);
1450 $MSignature=~s/\A.+\.(\w+\()/$1/g; # short name
1451 my $InvokedBy = $ClassMethod_AddedUsed{$TName}{$MFirst};
1452 return ($MSignature, $InvokedBy);
1453 }
1454
1455 return ();
1456}
1457
1458sub mergeTypes($$)
1459{
1460 my ($Type1_Id, $Type2_Id) = @_;
1461 return {} if(not $Type1_Id or not $Type2_Id);
1462
1463 if(defined $Cache{"mergeTypes"}{$Type1_Id}{$Type2_Id})
1464 { # already merged
1465 return $Cache{"mergeTypes"}{$Type1_Id}{$Type2_Id};
1466 }
1467
1468 my %Type1 = get_Type($Type1_Id, 1);
1469 my %Type2 = get_Type($Type2_Id, 2);
1470 if(isRecurType($Type1_Id, $Type2_Id))
1471 { # do not follow to recursive declarations
1472 return {};
1473 }
1474 return {} if(not $Type1{"Name"} or not $Type2{"Name"});
1475 return {} if(not $Type1{"Archive"} or not $Type2{"Archive"});
1476 return {} if($Type1{"Name"} ne $Type2{"Name"});
1477
1478 if(not classFilter(\%Type1, 1, 0)) {
1479 return {};
1480 }
1481
1482 $CheckedTypes{$Type1{"Name"}} = 1;
1483
1484 my %SubProblems = ();
1485
1486 if($Type1{"BaseType"} and $Type2{"BaseType"})
1487 { # check base type (arrays)
1488 return mergeTypes($Type1{"BaseType"}, $Type2{"BaseType"});
1489 }
1490
1491 if($Type2{"Type"}!~/(class|interface)/) {
1492 return {};
1493 }
1494
1495 if($Type1{"Type"} eq "class" and not $Class_Constructed{1}{$Type1_Id})
1496 { # class cannot be constructed or inherited by clients
1497 return {};
1498 }
1499
1500 if($Type1{"Type"} eq "class"
1501 and $Type2{"Type"} eq "interface")
1502 {
1503 %{$SubProblems{"Class_Became_Interface"}{""}}=(
1504 "Type_Name"=>$Type1{"Name"} );
1505
1506 return ($Cache{"mergeTypes"}{$Type1_Id}{$Type2_Id} = \%SubProblems);
1507 }
1508 if($Type1{"Type"} eq "interface"
1509 and $Type2{"Type"} eq "class")
1510 {
1511 %{$SubProblems{"Interface_Became_Class"}{""}}=(
1512 "Type_Name"=>$Type1{"Name"} );
1513
1514 return ($Cache{"mergeTypes"}{$Type1_Id}{$Type2_Id} = \%SubProblems);
1515 }
1516 if(not $Type1{"Final"}
1517 and $Type2{"Final"})
1518 {
1519 %{$SubProblems{"Class_Became_Final"}{""}}=(
1520 "Type_Name"=>$Type1{"Name"},
1521 "Target"=>$Type1{"Name"} );
1522 }
1523 if(not $Type1{"Abstract"}
1524 and $Type2{"Abstract"})
1525 {
1526 %{$SubProblems{"Class_Became_Abstract"}{""}}=(
1527 "Type_Name"=>$Type1{"Name"} );
1528 }
1529
1530 pushType($Type1_Id, $Type2_Id);
1531
1532 foreach my $AddedMethod (keys(%{$AddedMethod_Abstract{$Type1{"Name"}}}))
1533 {
1534 if($Type1{"Type"} eq "class")
1535 {
1536 if($Type1{"Abstract"})
1537 {
1538 if(my @InvokedBy = sort keys(%{$MethodUsed{2}{$AddedMethod}}))
1539 {
1540 %{$SubProblems{"Abstract_Class_Added_Abstract_Method_Invoked_By_Others"}{get_SFormat($AddedMethod)}} = (
1541 "Type_Name"=>$Type1{"Name"},
1542 "Type_Type"=>$Type1{"Type"},
1543 "Target"=>$AddedMethod,
1544 "InvokedBy"=>$InvokedBy[0] );
1545 }
1546 else
1547 {
1548 %{$SubProblems{"Abstract_Class_Added_Abstract_Method"}{get_SFormat($AddedMethod)}} = (
1549 "Type_Name"=>$Type1{"Name"},
1550 "Type_Type"=>$Type1{"Type"},
1551 "Target"=>$AddedMethod );
1552 }
1553 }
1554 else
1555 {
1556 %{$SubProblems{"NonAbstract_Class_Added_Abstract_Method"}{get_SFormat($AddedMethod)}} = (
1557 "Type_Name"=>$Type1{"Name"},
1558 "Type_Type"=>$Type1{"Type"},
1559 "Target"=>$AddedMethod );
1560 }
1561 }
1562 else
1563 {
1564 if(my @InvokedBy = sort keys(%{$MethodUsed{2}{$AddedMethod}}))
1565 {
1566 %{$SubProblems{"Interface_Added_Abstract_Method_Invoked_By_Others"}{get_SFormat($AddedMethod)}} = (
1567 "Type_Name"=>$Type1{"Name"},
1568 "Type_Type"=>$Type1{"Type"},
1569 "Target"=>$AddedMethod,
1570 "InvokedBy"=>$InvokedBy[0] );
1571 }
1572 else
1573 {
1574 %{$SubProblems{"Interface_Added_Abstract_Method"}{get_SFormat($AddedMethod)}} = (
1575 "Type_Name"=>$Type1{"Name"},
1576 "Type_Type"=>$Type1{"Type"},
1577 "Target"=>$AddedMethod );
1578 }
1579 }
1580 }
1581 foreach my $RemovedMethod (keys(%{$RemovedMethod_Abstract{$Type1{"Name"}}}))
1582 {
1583 if($Type1{"Type"} eq "class")
1584 {
1585 %{$SubProblems{"Class_Removed_Abstract_Method"}{get_SFormat($RemovedMethod)}} = (
1586 "Type_Name"=>$Type1{"Name"},
1587 "Type_Type"=>$Type1{"Type"},
1588 "Target"=>$RemovedMethod );
1589 }
1590 else
1591 {
1592 %{$SubProblems{"Interface_Removed_Abstract_Method"}{get_SFormat($RemovedMethod)}} = (
1593 "Type_Name"=>$Type1{"Name"},
1594 "Type_Type"=>$Type1{"Type"},
1595 "Target"=>$RemovedMethod );
1596 }
1597 }
1598 if($Type1{"Type"} eq "class"
1599 and $Type2{"Type"} eq "class")
1600 {
1601 my %SuperClass1 = get_Type($Type1{"SuperClass"}, 1);
1602 my %SuperClass2 = get_Type($Type2{"SuperClass"}, 2);
1603 if($SuperClass2{"Name"} ne $SuperClass1{"Name"})
1604 {
1605 if($SuperClass1{"Name"} eq "java.lang.Object"
1606 or not $SuperClass1{"Name"})
1607 {
1608 # Java 6: java.lang.Object
1609 # Java 7: none
1610 if($SuperClass2{"Name"} ne "java.lang.Object")
1611 {
1612 if($SuperClass2{"Abstract"}
1613 and $Type1{"Abstract"} and $Type2{"Abstract"}
1614 and keys(%{$Class_AbstractMethods{2}{$SuperClass2{"Name"}}}))
1615 {
1616 if(my ($Invoked, $InvokedBy) = getInvoked($Type1{"Name"}))
1617 {
1618 %{$SubProblems{"Abstract_Class_Added_Super_Abstract_Class_Invoked_By_Others"}{""}} = (
1619 "Type_Name"=>$Type1{"Name"},
1620 "Target"=>$SuperClass2{"Name"},
1621 "Invoked"=>$Invoked,
1622 "InvokedBy"=>$InvokedBy );
1623 }
1624 else
1625 {
1626 %{$SubProblems{"Abstract_Class_Added_Super_Abstract_Class"}{""}} = (
1627 "Type_Name"=>$Type1{"Name"},
1628 "Target"=>$SuperClass2{"Name"} );
1629 }
1630 }
1631 else
1632 {
1633 %{$SubProblems{"Added_Super_Class"}{""}} = (
1634 "Type_Name"=>$Type1{"Name"},
1635 "Target"=>$SuperClass2{"Name"} );
1636 }
1637 }
1638 }
1639 elsif($SuperClass2{"Name"} eq "java.lang.Object"
1640 or not $SuperClass2{"Name"})
1641 {
1642 # Java 6: java.lang.Object
1643 # Java 7: none
1644 if($SuperClass1{"Name"} ne "java.lang.Object")
1645 {
1646 %{$SubProblems{"Removed_Super_Class"}{""}} = (
1647 "Type_Name"=>$Type1{"Name"},
1648 "Target"=>$SuperClass1{"Name"} );
1649 }
1650 }
1651 else
1652 {
1653 %{$SubProblems{"Changed_Super_Class"}{""}} = (
1654 "Type_Name"=>$Type1{"Name"},
1655 "Target"=>$SuperClass1{"Name"},
1656 "Old_Value"=>$SuperClass1{"Name"},
1657 "New_Value"=>$SuperClass2{"Name"} );
1658 }
1659 }
1660 }
1661 my %SuperInterfaces_Old = map {get_TypeName($_, 1) => 1} keys(%{$Type1{"SuperInterface"}});
1662 my %SuperInterfaces_New = map {get_TypeName($_, 2) => 1} keys(%{$Type2{"SuperInterface"}});
1663 foreach my $SuperInterface (keys(%SuperInterfaces_New))
1664 {
1665 if(not $SuperInterfaces_Old{$SuperInterface})
1666 {
1667 my $HaveMethods = keys(%{$Class_AbstractMethods{2}{$SuperInterface}});
1668 my $HaveFields = keys(%{$Class_Fields{2}{$SuperInterface}});
1669
1670 if($Type1{"Type"} eq "interface")
1671 {
1672 if($HaveMethods
1673 or $SuperInterface=~/\Ajava\./)
1674 {
1675 if($HaveMethods and checkDefaultImpl(2, $SuperInterface, $Type2{"Name"}))
1676 {
1677 %{$SubProblems{"Interface_Added_Super_Interface_With_Implemented_Methods"}{get_SFormat($SuperInterface)}} = (
1678 "Type_Name"=>$Type1{"Name"},
1679 "Target"=>$SuperInterface );
1680 }
1681 else
1682 {
1683 if(my ($Invoked, $InvokedBy) = getInvoked($Type1{"Name"}))
1684 {
1685 %{$SubProblems{"Interface_Added_Super_Interface_Used_By_Others"}{get_SFormat($SuperInterface)}} = (
1686 "Type_Name"=>$Type1{"Name"},
1687 "Target"=>$SuperInterface,
1688 "Invoked"=>$Invoked,
1689 "InvokedBy"=>$InvokedBy );
1690 }
1691 else
1692 {
1693 %{$SubProblems{"Interface_Added_Super_Interface"}{get_SFormat($SuperInterface)}} = (
1694 "Type_Name"=>$Type1{"Name"},
1695 "Target"=>$SuperInterface );
1696 }
1697 }
1698 }
1699 elsif($HaveFields)
1700 {
1701 %{$SubProblems{"Interface_Added_Super_Constant_Interface"}{get_SFormat($SuperInterface)}} = (
1702 "Type_Name"=>$Type2{"Name"},
1703 "Target"=>$SuperInterface );
1704 }
1705 else
1706 {
1707 # empty interface
1708 }
1709 }
1710 else
1711 {
1712 if($Type1{"Abstract"} and $Type2{"Abstract"})
1713 {
1714 if($HaveMethods and checkDefaultImpl(2, $SuperInterface, $Type2{"Name"}))
1715 {
1716 %{$SubProblems{"Abstract_Class_Added_Super_Interface_With_Implemented_Methods"}{get_SFormat($SuperInterface)}} = (
1717 "Type_Name"=>$Type1{"Name"},
1718 "Target"=>$SuperInterface );
1719 }
1720 else
1721 {
1722 if(my ($Invoked, $InvokedBy) = getInvoked($Type1{"Name"}))
1723 {
1724 %{$SubProblems{"Abstract_Class_Added_Super_Interface_Invoked_By_Others"}{get_SFormat($SuperInterface)}} = (
1725 "Type_Name"=>$Type1{"Name"},
1726 "Target"=>$SuperInterface,
1727 "Invoked"=>$Invoked,
1728 "InvokedBy"=>$InvokedBy );
1729 }
1730 else
1731 {
1732 %{$SubProblems{"Abstract_Class_Added_Super_Interface"}{get_SFormat($SuperInterface)}} = (
1733 "Type_Name"=>$Type1{"Name"},
1734 "Target"=>$SuperInterface );
1735 }
1736 }
1737 }
1738 }
1739 }
1740 }
1741 foreach my $SuperInterface (keys(%SuperInterfaces_Old))
1742 {
1743 if(not $SuperInterfaces_New{$SuperInterface})
1744 {
1745 my $HaveMethods = keys(%{$Class_AbstractMethods{1}{$SuperInterface}});
1746 my $HaveFields = keys(%{$Class_Fields{1}{$SuperInterface}});
1747
1748 if($Type1{"Type"} eq "interface")
1749 {
1750 if($HaveMethods
1751 or $SuperInterface=~/\Ajava\./)
1752 {
1753 %{$SubProblems{"Interface_Removed_Super_Interface"}{get_SFormat($SuperInterface)}} = (
1754 "Type_Name"=>$Type1{"Name"},
1755 "Type_Type"=>"interface",
1756 "Target"=>$SuperInterface );
1757 }
1758 elsif($HaveFields)
1759 {
1760 %{$SubProblems{"Interface_Removed_Super_Constant_Interface"}{get_SFormat($SuperInterface)}} = (
1761 "Type_Name"=>$Type1{"Name"},
1762 "Target"=>$SuperInterface );
1763 }
1764 else {
1765 # empty interface
1766 }
1767 }
1768 else
1769 {
1770 %{$SubProblems{"Class_Removed_Super_Interface"}{get_SFormat($SuperInterface)}} = (
1771 "Type_Name"=>$Type1{"Name"},
1772 "Type_Type"=>"class",
1773 "Target"=>$SuperInterface );
1774 }
1775 }
1776 }
1777
1778 foreach my $Field_Name (sort keys(%{$Type1{"Fields"}}))
1779 {# check older fields
1780 my $Access1 = $Type1{"Fields"}{$Field_Name}{"Access"};
1781 if($Access1=~/private/) {
1782 next;
1783 }
1784
1785 my $Field_Pos1 = $Type1{"Fields"}{$Field_Name}{"Pos"};
1786 my $FieldType1_Id = $Type1{"Fields"}{$Field_Name}{"Type"};
1787 my %FieldType1 = get_Type($FieldType1_Id, 1);
1788
1789 if(not $Type2{"Fields"}{$Field_Name})
1790 {# removed fields
1791 my $StraightPair_Name = findFieldPair($Field_Pos1, \%Type2);
1792 if($StraightPair_Name ne "lost" and not $Type1{"Fields"}{$StraightPair_Name}
1793 and $FieldType1{"Name"} eq get_TypeName($Type2{"Fields"}{$StraightPair_Name}{"Type"}, 2))
1794 {
1795 if(my $Constant = get_ConstantValue($Type1{"Fields"}{$Field_Name}{"Value"}, $FieldType1{"Name"}))
1796 {
1797 %{$SubProblems{"Renamed_Constant_Field"}{$Field_Name}}=(
1798 "Target"=>$Field_Name,
1799 "Type_Name"=>$Type1{"Name"},
1800 "Old_Value"=>$Field_Name,
1801 "New_Value"=>$StraightPair_Name,
1802 "Field_Type"=>$FieldType1{"Name"},
1803 "Field_Value"=>$Constant );
1804 }
1805 else
1806 {
1807 %{$SubProblems{"Renamed_Field"}{$Field_Name}}=(
1808 "Target"=>$Field_Name,
1809 "Type_Name"=>$Type1{"Name"},
1810 "Old_Value"=>$Field_Name,
1811 "New_Value"=>$StraightPair_Name,
1812 "Field_Type"=>$FieldType1{"Name"} );
1813 }
1814 }
1815 else
1816 {
1817 if(my $Constant = get_ConstantValue($Type1{"Fields"}{$Field_Name}{"Value"}, $FieldType1{"Name"}))
1818 { # has a compile-time constant value
1819 %{$SubProblems{"Removed_Constant_Field"}{$Field_Name}}=(
1820 "Target"=>$Field_Name,
1821 "Type_Name"=>$Type1{"Name"},
1822 "Field_Value"=>$Constant,
1823 "Field_Type"=>$FieldType1{"Name"},
1824 "Type_Type"=>$Type1{"Type"} );
1825 }
1826 else
1827 {
1828 %{$SubProblems{"Removed_NonConstant_Field"}{$Field_Name}}=(
1829 "Target"=>$Field_Name,
1830 "Type_Name"=>$Type1{"Name"},
1831 "Type_Type"=>$Type1{"Type"},
1832 "Field_Type"=>$FieldType1{"Name"} );
1833 }
1834 }
1835 next;
1836 }
1837 my $FieldType2_Id = $Type2{"Fields"}{$Field_Name}{"Type"};
1838 my %FieldType2 = get_Type($FieldType2_Id, 2);
1839
1840 if(not $Type1{"Fields"}{$Field_Name}{"Static"}
1841 and $Type2{"Fields"}{$Field_Name}{"Static"})
1842 {
1843 if(not $Type1{"Fields"}{$Field_Name}{"Value"})
1844 {
1845 %{$SubProblems{"NonConstant_Field_Became_Static"}{$Field_Name}}=(
1846 "Target"=>$Field_Name,
1847 "Field_Type"=>$FieldType1{"Name"},
1848 "Type_Name"=>$Type1{"Name"} );
1849 }
1850 }
1851 elsif($Type1{"Fields"}{$Field_Name}{"Static"}
1852 and not $Type2{"Fields"}{$Field_Name}{"Static"})
1853 {
1854 if($Type1{"Fields"}{$Field_Name}{"Value"})
1855 {
1856 %{$SubProblems{"Constant_Field_Became_NonStatic"}{$Field_Name}}=(
1857 "Target"=>$Field_Name,
1858 "Field_Type"=>$FieldType1{"Name"},
1859 "Type_Name"=>$Type1{"Name"} );
1860 }
1861 else
1862 {
1863 %{$SubProblems{"NonConstant_Field_Became_NonStatic"}{$Field_Name}}=(
1864 "Target"=>$Field_Name,
1865 "Field_Type"=>$FieldType1{"Name"},
1866 "Type_Name"=>$Type1{"Name"} );
1867 }
1868 }
1869 if(not $Type1{"Fields"}{$Field_Name}{"Final"}
1870 and $Type2{"Fields"}{$Field_Name}{"Final"})
1871 {
1872 %{$SubProblems{"Field_Became_Final"}{$Field_Name}}=(
1873 "Target"=>$Field_Name,
1874 "Field_Type"=>$FieldType1{"Name"},
1875 "Type_Name"=>$Type1{"Name"} );
1876 }
1877 elsif($Type1{"Fields"}{$Field_Name}{"Final"}
1878 and not $Type2{"Fields"}{$Field_Name}{"Final"})
1879 {
1880 %{$SubProblems{"Field_Became_NonFinal"}{$Field_Name}}=(
1881 "Target"=>$Field_Name,
1882 "Field_Type"=>$FieldType1{"Name"},
1883 "Type_Name"=>$Type1{"Name"} );
1884 }
1885 my $Access2 = $Type2{"Fields"}{$Field_Name}{"Access"};
1886 if($Access1 eq "public" and $Access2=~/protected|private/
1887 or $Access1 eq "protected" and $Access2=~/private/)
1888 {
1889 %{$SubProblems{"Changed_Field_Access"}{$Field_Name}}=(
1890 "Target"=>$Field_Name,
1891 "Type_Name"=>$Type1{"Name"},
1892 "Old_Value"=>$Access1,
1893 "New_Value"=>$Access2 );
1894 }
1895
1896 my $Value1 = get_ConstantValue($Type1{"Fields"}{$Field_Name}{"Value"}, $FieldType1{"Name"});
1897 my $Value2 = get_ConstantValue($Type2{"Fields"}{$Field_Name}{"Value"}, $FieldType2{"Name"});
1898
1899 if($Value1 ne $Value2)
1900 {
1901 if($Value1 and $Value2)
1902 {
1903 if($Type1{"Fields"}{$Field_Name}{"Final"}
1904 and $Type2{"Fields"}{$Field_Name}{"Final"})
1905 {
1906 if($Field_Name=~/(\A|_)(VERSION|VERNUM|BUILDNUMBER|BUILD)(_|\Z)/i)
1907 {
1908 %{$SubProblems{"Changed_Final_Version_Field_Value"}{$Field_Name}}=(
1909 "Target"=>$Field_Name,
1910 "Field_Type"=>$FieldType1{"Name"},
1911 "Type_Name"=>$Type1{"Name"},
1912 "Old_Value"=>$Value1,
1913 "New_Value"=>$Value2 );
1914 }
1915 else
1916 {
1917 %{$SubProblems{"Changed_Final_Field_Value"}{$Field_Name}}=(
1918 "Target"=>$Field_Name,
1919 "Field_Type"=>$FieldType1{"Name"},
1920 "Type_Name"=>$Type1{"Name"},
1921 "Old_Value"=>$Value1,
1922 "New_Value"=>$Value2 );
1923 }
1924 }
1925 }
1926 }
1927
1928 my %Sub_SubChanges = detectTypeChange($FieldType1_Id, $FieldType2_Id, "Field");
1929 foreach my $Sub_SubProblemType (keys(%Sub_SubChanges))
1930 {
1931 %{$SubProblems{$Sub_SubProblemType}{$Field_Name}}=(
1932 "Target"=>$Field_Name,
1933 "Type_Name"=>$Type1{"Name"});
1934
1935 foreach my $Attr (keys(%{$Sub_SubChanges{$Sub_SubProblemType}}))
1936 {
1937 $SubProblems{$Sub_SubProblemType}{$Field_Name}{$Attr} = $Sub_SubChanges{$Sub_SubProblemType}{$Attr};
1938 }
1939 }
1940
1941 if($FieldType1_Id and $FieldType2_Id)
1942 { # check field type change
1943 my $Sub_SubProblems = mergeTypes($FieldType1_Id, $FieldType2_Id);
1944 my %DupProblems = ();
1945
1946 foreach my $Sub_SubProblemType (sort keys(%{$Sub_SubProblems}))
1947 {
1948 foreach my $Sub_SubLocation (sort {length($a)<=>length($b)} sort keys(%{$Sub_SubProblems->{$Sub_SubProblemType}}))
1949 {
1950 if(not defined $AllAffected)
1951 {
1952 if(defined $DupProblems{$Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation}}) {
1953 next;
1954 }
1955 }
1956
1957 my $NewLocation = ($Sub_SubLocation)?$Field_Name.".".$Sub_SubLocation:$Field_Name;
1958 $SubProblems{$Sub_SubProblemType}{$NewLocation} = $Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation};
1959
1960 if(not defined $AllAffected)
1961 {
1962 $DupProblems{$Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation}} = 1;
1963 }
1964 }
1965 }
1966 %DupProblems = ();
1967 }
1968 }
1969
1970 foreach my $Field_Name (sort keys(%{$Type2{"Fields"}}))
1971 { # check added fields
1972 if($Type2{"Fields"}{$Field_Name}{"Access"}=~/private/) {
1973 next;
1974 }
1975 my $FieldPos2 = $Type2{"Fields"}{$Field_Name}{"Pos"};
1976 my $FieldType2_Id = $Type2{"Fields"}{$Field_Name}{"Type"};
1977 my %FieldType2 = get_Type($FieldType2_Id, 2);
1978
1979 if(not $Type1{"Fields"}{$Field_Name})
1980 {# added fields
1981 my $StraightPair_Name = findFieldPair($FieldPos2, \%Type1);
1982 if($StraightPair_Name ne "lost" and not $Type2{"Fields"}{$StraightPair_Name}
1983 and get_TypeName($Type1{"Fields"}{$StraightPair_Name}{"Type"}, 1) eq $FieldType2{"Name"})
1984 {
1985 # Already reported as "Renamed_Field" or "Renamed_Constant_Field"
1986 }
1987 else
1988 {
1989 if($Type1{"Type"} eq "interface")
1990 {
1991 %{$SubProblems{"Interface_Added_Field"}{$Field_Name}}=(
1992 "Target"=>$Field_Name,
1993 "Type_Name"=>$Type1{"Name"} );
1994 }
1995 else
1996 {
1997 %{$SubProblems{"Class_Added_Field"}{$Field_Name}}=(
1998 "Target"=>$Field_Name,
1999 "Type_Name"=>$Type1{"Name"} );
2000 }
2001 }
2002 }
2003 }
2004
2005 pop(@RecurTypes);
2006 return ($Cache{"mergeTypes"}{$Type1_Id}{$Type2_Id} = \%SubProblems);
2007}
2008
2009sub checkDefaultImpl($$$)
2010{ # Check if all abstract methods of the super class have
2011 # default implementations in the class
2012 my ($LibVersion, $SuperClassName, $ClassName) = @_;
2013
2014 foreach my $Method (keys(%{$Class_AbstractMethods{$LibVersion}{$SuperClassName}}))
2015 {
2016 if(my $Overridden = findMethod_Class($Method, $ClassName, $LibVersion))
2017 {
2018 if($MethodInfo{$LibVersion}{$Overridden}{"Abstract"}) {
2019 return 0;
2020 }
2021 }
2022 else {
2023 return 0;
2024 }
2025 }
2026
2027 return 1;
2028}
2029
2030sub unmangle($)
2031{
2032 my $Name = $_[0];
2033 $Name=~s!/!.!g;
2034 $Name=~s!:\(!(!g;
2035 $Name=~s!\).+\Z!)!g;
2036 if($Name=~/\A(.+)\((.+)\)/)
2037 {
2038 my ($ShortName, $MangledParams) = ($1, $2);
2039 my @UnmangledParams = ();
2040 my ($IsArray, $Shift, $Pos, $CurParam) = (0, 0, 0, "");
2041 while($Pos<length($MangledParams))
2042 {
2043 my $Symbol = substr($MangledParams, $Pos, 1);
2044 if($Symbol eq "[")
2045 { # array
2046 $IsArray = 1;
2047 $Pos+=1;
2048 }
2049 elsif($Symbol eq "L")
2050 { # class
2051 if(substr($MangledParams, $Pos+1)=~/\A(.+?);/) {
2052 $CurParam = $1;
2053 $Shift = length($CurParam)+2;
2054 }
2055 if($IsArray) {
2056 $CurParam .= "[]";
2057 }
2058 $Pos+=$Shift;
2059 push(@UnmangledParams, $CurParam);
2060 ($IsArray, $Shift, $CurParam) = (0, 0, "")
2061 }
2062 else
2063 {
2064 if($Symbol eq "C") {
2065 $CurParam = "char";
2066 }
2067 elsif($Symbol eq "B") {
2068 $CurParam = "byte";
2069 }
2070 elsif($Symbol eq "S") {
2071 $CurParam = "short";
2072 }
2073 elsif($Symbol eq "S") {
2074 $CurParam = "short";
2075 }
2076 elsif($Symbol eq "I") {
2077 $CurParam = "int";
2078 }
2079 elsif($Symbol eq "F") {
2080 $CurParam = "float";
2081 }
2082 elsif($Symbol eq "J") {
2083 $CurParam = "long";
2084 }
2085 elsif($Symbol eq "D") {
2086 $CurParam = "double";
2087 }
2088 else {
2089 printMsg("INFO", "WARNING: unmangling error");
2090 }
2091 if($IsArray) {
2092 $CurParam .= "[]";
2093 }
2094 $Pos+=1;
2095 push(@UnmangledParams, $CurParam);
2096 ($IsArray, $Shift, $CurParam) = (0, 0, "")
2097 }
2098 }
2099 return $ShortName."(".join(", ", @UnmangledParams).")";
2100 }
2101 else {
2102 return $Name;
2103 }
2104}
2105
2106sub get_TypeName($$)
2107{
2108 my ($TypeId, $LibVersion) = @_;
2109 return $TypeInfo{$LibVersion}{$TypeId}{"Name"};
2110}
2111
2112sub get_ShortName($$)
2113{
2114 my ($TypeId, $LibVersion) = @_;
2115 my $TypeName = $TypeInfo{$LibVersion}{$TypeId}{"Name"};
2116 $TypeName=~s/\A.*\.//g;
2117 return $TypeName;
2118}
2119
2120sub get_TypeType($$)
2121{
2122 my ($TypeId, $LibVersion) = @_;
2123 return $TypeInfo{$LibVersion}{$TypeId}{"Type"};
2124}
2125
2126sub get_TypeHeader($$)
2127{
2128 my ($TypeId, $LibVersion) = @_;
2129 return $TypeInfo{$LibVersion}{$TypeId}{"Header"};
2130}
2131
2132sub get_BaseType($$)
2133{
2134 my ($TypeId, $LibVersion) = @_;
2135 return "" if(not $TypeId);
2136 if(defined $Cache{"get_BaseType"}{$TypeId}{$LibVersion}) {
2137 return %{$Cache{"get_BaseType"}{$TypeId}{$LibVersion}};
2138 }
2139 return "" if(not $TypeInfo{$LibVersion}{$TypeId});
2140 my %Type = %{$TypeInfo{$LibVersion}{$TypeId}};
2141 return %Type if(not $Type{"BaseType"});
2142 %Type = get_BaseType($Type{"BaseType"}, $LibVersion);
2143 $Cache{"get_BaseType"}{$TypeId}{$LibVersion} = \%Type;
2144 return %Type;
2145}
2146
2147sub get_OneStep_BaseType($$)
2148{
2149 my ($TypeId, $LibVersion) = @_;
2150 return "" if(not $TypeId);
2151 return "" if(not $TypeInfo{$LibVersion}{$TypeId});
2152 my %Type = %{$TypeInfo{$LibVersion}{$TypeId}};
2153 return %Type if(not $Type{"BaseType"});
2154 return get_Type($Type{"BaseType"}, $LibVersion);
2155}
2156
2157sub get_Type($$)
2158{
2159 my ($TypeId, $LibVersion) = @_;
2160 return "" if(not $TypeId);
2161 return "" if(not $TypeInfo{$LibVersion}{$TypeId});
2162 return %{$TypeInfo{$LibVersion}{$TypeId}};
2163}
2164
2165sub classFilter($$$)
2166{
2167 my ($Class, $LibVersion, $ClassContext) = @_;
2168
2169 if(defined $Class->{"Dep"}) {
2170 return 0;
2171 }
2172
2173 my $CName = $Class->{"Name"};
2174 my $Package = $Class->{"Package"};
2175
2176 if(defined $ClassListPath
2177 and not defined $ClassList_User{$CName})
2178 { # user defined classes
2179 return 0;
2180 }
2181
2182 if(defined $SkipClassesList
2183 and defined $SkipClasses{$CName})
2184 { # user defined classes
2185 return 0;
2186 }
2187
2188 if(skipPackage($Package, $LibVersion))
2189 { # internal packages
2190 return 0;
2191 }
2192
2193 if(skipType($CName))
2194 { # internal types
2195 return 0;
2196 }
2197
2198 if($ClassContext)
2199 {
2200 my @Ann = ();
2201
2202 if(defined $Class->{"Annotations"}) {
2203 @Ann = keys(%{$Class->{"Annotations"}});
2204 }
2205
2206 if(not annotationFilter(\@Ann, $LibVersion)) {
2207 return 0;
2208 }
2209
2210 if($ClientPath)
2211 {
2212 if(not defined $UsedClasses_Client{$CName}) {
2213 return 0;
2214 }
2215 }
2216 }
2217
2218 return 1;
2219}
2220
2221sub annotationFilter($$)
2222{
2223 my ($Ann, $LibVersion) = @_;
2224
2225 if(not defined $CountMethods)
2226 {
2227 if(defined $AddedAnnotations and $LibVersion==1) {
2228 return 1;
2229 }
2230
2231 if(defined $RemovedAnnotations and $LibVersion==2) {
2232 return 1;
2233 }
2234 }
2235
2236 if($SkipAnnotationsListPath)
2237 {
2238 foreach my $Aid (@{$Ann})
2239 {
2240 my $AName = $TypeInfo{$LibVersion}{$Aid}{"Name"};
2241
2242 if(defined $SkipAnnotationList_User{$AName}) {
2243 return 0;
2244 }
2245 }
2246 }
2247
2248 if($AnnotationsListPath)
2249 {
2250 my $Annotated = 0;
2251
2252 foreach my $Aid (@{$Ann})
2253 {
2254 my $AName = $TypeInfo{$LibVersion}{$Aid}{"Name"};
2255
2256 if(defined $AnnotationList_User{$AName})
2257 {
2258 $Annotated = 1;
2259 last;
2260 }
2261 }
2262
2263 if(not $Annotated) {
2264 return 0;
2265 }
2266 }
2267
2268 return 1;
2269}
2270
2271sub methodFilter($$)
2272{
2273 my ($Method, $LibVersion) = @_;
2274
2275 if(defined $MethodInfo{$LibVersion}{$Method}{"Dep"}) {
2276 return 0;
2277 }
2278
2279 if($MethodInfo{$LibVersion}{$Method}{"Access"}=~/private/)
2280 { # non-public
2281 return 0;
2282 }
2283
2284 my $ClassId = $MethodInfo{$LibVersion}{$Method}{"Class"};
2285 my %Class = get_Type($ClassId, $LibVersion);
2286
2287 if($Class{"Access"}=~/private/)
2288 { # skip private classes
2289 return 0;
2290 }
2291
2292 my $Package = $MethodInfo{$LibVersion}{$Method}{"Package"};
2293
2294 my @Ann = ();
2295
2296 if(defined $Class{"Annotations"}) {
2297 @Ann = (@Ann, keys(%{$Class{"Annotations"}}));
2298 }
2299
2300 if(defined $MethodInfo{$LibVersion}{$Method}{"Annotations"}) {
2301 @Ann = (@Ann, keys(%{$MethodInfo{$LibVersion}{$Method}{"Annotations"}}));
2302 }
2303
2304 if(not annotationFilter(\@Ann, $LibVersion)) {
2305 return 0;
2306 }
2307
2308 if($ClientPath)
2309 { # user defined application
2310 if(not defined $UsedMethods_Client{$Method}
2311 and not defined $UsedClasses_Client{$Class{"Name"}}) {
2312 return 0;
2313 }
2314 }
2315
2316 if(skipPackage($Package, $LibVersion))
2317 { # internal packages
2318 return 0;
2319 }
2320
2321 if(not classFilter(\%Class, $LibVersion, 0)) {
2322 return 0;
2323 }
2324
2325 if(defined $SkipDeprecated)
2326 {
2327 if($Class{"Deprecated"})
2328 { # deprecated class
2329 return 0;
2330 }
2331 if($MethodInfo{$LibVersion}{$Method}{"Deprecated"})
2332 { # deprecated method
2333 return 0;
2334 }
2335 }
2336
2337 return 1;
2338}
2339
2340sub skipType($)
2341{
2342 my $TName = $_[0];
2343
2344 if(defined $SkipInternalTypes)
2345 {
2346 if($TName=~/($SkipInternalTypes)/) {
2347 return 1;
2348 }
2349 }
2350
2351 return 0;
2352}
2353
2354sub skipPackage($$)
2355{
2356 my ($Package, $LibVersion) = @_;
2357 return 0 if(not $Package);
2358
2359 if(defined $SkipInternalPackages)
2360 {
2361 if($Package=~/($SkipInternalPackages)/) {
2362 return 1;
2363 }
2364 }
2365
2366 if(defined $SkipPackages{$LibVersion})
2367 {
2368 foreach my $SkipPackage (keys(%{$SkipPackages{$LibVersion}}))
2369 {
2370 if($Package=~/\A\Q$SkipPackage\E(\.|\Z)/)
2371 { # user skipped packages
2372 return 1;
2373 }
2374 }
2375 }
2376
2377 if(not defined $KeepInternal)
2378 {
2379 my $Note = (not keys %SkippedPackage)?" (use --keep-internal option to check them)":"";
2380
2381 if($Package=~/(\A|\.)(internal|impl|examples)(\.|\Z)/)
2382 { # internal packages
2383 if(not $SkippedPackage{$LibVersion}{$2})
2384 {
2385 $SkippedPackage{$LibVersion}{$2} = 1;
2386 printMsg("WARNING", "skip \"$2\" packages".$Note);
2387 }
2388 return 1;
2389 }
2390 }
2391
2392 if(defined $KeepPackages{$LibVersion}
2393 and my @Keeped = keys(%{$KeepPackages{$LibVersion}}))
2394 {
2395 my $UserKeeped = 0;
2396 foreach my $KeepPackage (@Keeped)
2397 {
2398 if($Package=~/\A\Q$KeepPackage\E(\.|\Z)/)
2399 { # user keeped packages
2400 $UserKeeped = 1;
2401 last;
2402 }
2403 }
2404 if(not $UserKeeped) {
2405 return 1;
2406 }
2407 }
2408
2409 return 0;
2410}
2411
2412sub get_MSuffix($)
2413{
2414 my $Method = $_[0];
2415 if($Method=~/(\(.*\))/) {
2416 return $1;
2417 }
2418 return "";
2419}
2420
2421sub get_MShort($)
2422{
2423 my $Method = $_[0];
2424 if($Method=~/([^\.]+)\:\(/) {
2425 return $1;
2426 }
2427 return "";
2428}
2429
2430sub findMethod($$$$)
2431{
2432 my ($Method, $MethodVersion, $ClassName, $ClassVersion) = @_;
2433 my $ClassId = $TName_Tid{$ClassVersion}{$ClassName};
2434
2435 if($ClassId)
2436 {
2437 my @Search = ();
2438 if(get_TypeType($ClassId, $ClassVersion) eq "class")
2439 {
2440 if(my $SuperClassId = $TypeInfo{$ClassVersion}{$ClassId}{"SuperClass"}) {
2441 push(@Search, $SuperClassId);
2442 }
2443 }
2444
2445 if(not defined $MethodInfo{$MethodVersion}{$Method}
2446 or $MethodInfo{$MethodVersion}{$Method}{"Abstract"})
2447 {
2448 if(my @SuperInterfaces = sort keys(%{$TypeInfo{$ClassVersion}{$ClassId}{"SuperInterface"}})) {
2449 push(@Search, @SuperInterfaces);
2450 }
2451 }
2452
2453 foreach my $SuperId (@Search)
2454 {
2455 if($SuperId eq $ClassId) {
2456 next;
2457 }
2458
2459 my $SuperName = get_TypeName($SuperId, $ClassVersion);
2460
2461 if(my $MethodInClass = findMethod_Class($Method, $SuperName, $ClassVersion)) {
2462 return $MethodInClass;
2463 }
2464 elsif(my $MethodInSuperClasses = findMethod($Method, $MethodVersion, $SuperName, $ClassVersion)) {
2465 return $MethodInSuperClasses;
2466 }
2467 }
2468 }
2469
2470 my $TargetSuffix = get_MSuffix($Method);
2471 my $TargetShortName = get_MShort($Method);
2472
2473 # search in java.lang.Object
2474 foreach my $C (keys(%JavaObjectMethod))
2475 {
2476 if($TargetSuffix eq get_MSuffix($C))
2477 {
2478 if($TargetShortName eq get_MShort($C)) {
2479 return $C;
2480 }
2481 }
2482 }
2483
2484 return undef;
2485}
2486
2487sub findMethod_Class($$$)
2488{
2489 my ($Method, $ClassName, $ClassVersion) = @_;
2490 my $TargetSuffix = get_MSuffix($Method);
2491 my $TargetShortName = get_MShort($Method);
2492
2493 if(not defined $Class_Methods{$ClassVersion}{$ClassName}) {
2494 return undef;
2495 }
2496
2497 foreach my $Candidate (sort keys(%{$Class_Methods{$ClassVersion}{$ClassName}}))
2498 { # search for method with the same parameters suffix
2499 next if($MethodInfo{$ClassVersion}{$Candidate}{"Constructor"});
2500 if($TargetSuffix eq get_MSuffix($Candidate))
2501 {
2502 if($TargetShortName eq get_MShort($Candidate)) {
2503 return $Candidate;
2504 }
2505 }
2506 }
2507
2508 return undef;
2509}
2510
2511sub prepareMethods($)
2512{
2513 my $LibVersion = $_[0];
2514 foreach my $Method (keys(%{$MethodInfo{$LibVersion}}))
2515 {
2516 if($MethodInfo{$LibVersion}{$Method}{"Access"}!~/private/)
2517 {
2518 if($MethodInfo{$LibVersion}{$Method}{"Constructor"}) {
2519 registerUsage($MethodInfo{$LibVersion}{$Method}{"Class"}, $LibVersion);
2520 }
2521 else {
2522 registerUsage($MethodInfo{$LibVersion}{$Method}{"Return"}, $LibVersion);
2523 }
2524 }
2525 }
2526}
2527
2528sub mergeMethods()
2529{
2530 foreach my $Method (sort keys(%{$MethodInfo{1}}))
2531 { # compare methods
2532 next if(not defined $MethodInfo{2}{$Method});
2533
2534 if(not $MethodInfo{1}{$Method}{"Archive"}
2535 or not $MethodInfo{2}{$Method}{"Archive"}) {
2536 next;
2537 }
2538
2539 if(not methodFilter($Method, 1)) {
2540 next;
2541 }
2542
2543 my $ClassId1 = $MethodInfo{1}{$Method}{"Class"};
2544 my %Class1 = get_Type($ClassId1, 1);
2545
2546 $CheckedTypes{$Class1{"Name"}} = 1;
2547 $CheckedMethods{$Method} = 1;
2548
2549 my %Class2 = get_Type($MethodInfo{2}{$Method}{"Class"}, 2);
2550 if(not $MethodInfo{1}{$Method}{"Static"}
2551 and $Class1{"Type"} eq "class" and not $Class_Constructed{1}{$ClassId1})
2552 { # class cannot be constructed or inherited by clients
2553 # non-static method cannot be called
2554 next;
2555 }
2556 # checking attributes
2557 if(not $MethodInfo{1}{$Method}{"Static"}
2558 and $MethodInfo{2}{$Method}{"Static"}) {
2559 %{$CompatProblems{$Method}{"Method_Became_Static"}{""}} = ();
2560 }
2561 elsif($MethodInfo{1}{$Method}{"Static"}
2562 and not $MethodInfo{2}{$Method}{"Static"}) {
2563 %{$CompatProblems{$Method}{"Method_Became_NonStatic"}{""}} = ();
2564 }
2565 if(not $MethodInfo{1}{$Method}{"Synchronized"}
2566 and $MethodInfo{2}{$Method}{"Synchronized"}) {
2567 %{$CompatProblems{$Method}{"Method_Became_Synchronized"}{""}} = ();
2568 }
2569 elsif($MethodInfo{1}{$Method}{"Synchronized"}
2570 and not $MethodInfo{2}{$Method}{"Synchronized"}) {
2571 %{$CompatProblems{$Method}{"Method_Became_NonSynchronized"}{""}} = ();
2572 }
2573 if(not $MethodInfo{1}{$Method}{"Final"}
2574 and $MethodInfo{2}{$Method}{"Final"})
2575 {
2576 if($MethodInfo{1}{$Method}{"Static"}) {
2577 %{$CompatProblems{$Method}{"Static_Method_Became_Final"}{""}} = ();
2578 }
2579 else {
2580 %{$CompatProblems{$Method}{"NonStatic_Method_Became_Final"}{""}} = ();
2581 }
2582 }
2583 my $Access1 = $MethodInfo{1}{$Method}{"Access"};
2584 my $Access2 = $MethodInfo{2}{$Method}{"Access"};
2585 if($Access1 eq "public" and $Access2=~/protected|private/
2586 or $Access1 eq "protected" and $Access2=~/private/)
2587 {
2588 %{$CompatProblems{$Method}{"Changed_Method_Access"}{""}} = (
2589 "Old_Value"=>$Access1,
2590 "New_Value"=>$Access2 );
2591 }
2592 if($Class1{"Type"} eq "class"
2593 and $Class2{"Type"} eq "class")
2594 {
2595 if(not $MethodInfo{1}{$Method}{"Abstract"}
2596 and $MethodInfo{2}{$Method}{"Abstract"})
2597 {
2598 %{$CompatProblems{$Method}{"Method_Became_Abstract"}{""}} = ();
2599 %{$CompatProblems{$Method}{"Class_Method_Became_Abstract"}{"this.".get_SFormat($Method)}} = (
2600 "Type_Name"=>$Class1{"Name"},
2601 "Target"=>$Method );
2602 }
2603 elsif($MethodInfo{1}{$Method}{"Abstract"}
2604 and not $MethodInfo{2}{$Method}{"Abstract"})
2605 {
2606 %{$CompatProblems{$Method}{"Method_Became_NonAbstract"}{""}} = ();
2607 %{$CompatProblems{$Method}{"Class_Method_Became_NonAbstract"}{"this.".get_SFormat($Method)}} = (
2608 "Type_Name"=>$Class1{"Name"},
2609 "Target"=>$Method );
2610 }
2611 }
2612 elsif($Class1{"Type"} eq "interface"
2613 and $Class2{"Type"} eq "interface")
2614 {
2615 if(not $MethodInfo{1}{$Method}{"Abstract"}
2616 and $MethodInfo{2}{$Method}{"Abstract"})
2617 {
2618 %{$CompatProblems{$Method}{"Method_Became_NonDefault"}{""}} = ();
2619 %{$CompatProblems{$Method}{"Interface_Method_Became_NonDefault"}{"this.".get_SFormat($Method)}} = (
2620 "Type_Name"=>$Class1{"Name"},
2621 "Target"=>$Method );
2622 }
2623 elsif($MethodInfo{1}{$Method}{"Abstract"}
2624 and not $MethodInfo{2}{$Method}{"Abstract"})
2625 {
2626 %{$CompatProblems{$Method}{"Method_Became_Default"}{""}} = ();
2627 %{$CompatProblems{$Method}{"Interface_Method_Became_Default"}{"this.".get_SFormat($Method)}} = (
2628 "Type_Name"=>$Class1{"Name"},
2629 "Target"=>$Method );
2630 }
2631 }
2632
2633 my %Exceptions_Old = map {get_TypeName($_, 1) => $_} keys(%{$MethodInfo{1}{$Method}{"Exceptions"}});
2634 my %Exceptions_New = map {get_TypeName($_, 2) => $_} keys(%{$MethodInfo{2}{$Method}{"Exceptions"}});
2635 foreach my $Exception (keys(%Exceptions_Old))
2636 {
2637 if(not $Exceptions_New{$Exception})
2638 {
2639 my %ExceptionType = get_Type($Exceptions_Old{$Exception}, 1);
2640 my $SuperClass = $ExceptionType{"SuperClass"};
2641 if($KnownRuntimeExceptions{$Exception}
2642 or defined $SuperClass and get_TypeName($SuperClass, 1) eq "java.lang.RuntimeException")
2643 {
2644 if(not $MethodInfo{1}{$Method}{"Abstract"}
2645 and not $MethodInfo{2}{$Method}{"Abstract"})
2646 {
2647 %{$CompatProblems{$Method}{"Removed_Unchecked_Exception"}{"this.".get_SFormat($Exception)}} = (
2648 "Type_Name"=>$Class1{"Name"},
2649 "Target"=>$Exception );
2650 }
2651 }
2652 else
2653 {
2654 if($MethodInfo{1}{$Method}{"Abstract"}
2655 and $MethodInfo{2}{$Method}{"Abstract"})
2656 {
2657 %{$CompatProblems{$Method}{"Abstract_Method_Removed_Checked_Exception"}{"this.".get_SFormat($Exception)}} = (
2658 "Type_Name"=>$Class1{"Name"},
2659 "Target"=>$Exception );
2660 }
2661 else
2662 {
2663 %{$CompatProblems{$Method}{"NonAbstract_Method_Removed_Checked_Exception"}{"this.".get_SFormat($Exception)}} = (
2664 "Type_Name"=>$Class1{"Name"},
2665 "Target"=>$Exception );
2666 }
2667 }
2668 }
2669 }
2670 foreach my $Exception (keys(%Exceptions_New))
2671 {
2672 if(not $Exceptions_Old{$Exception})
2673 {
2674 my %ExceptionType = get_Type($Exceptions_New{$Exception}, 2);
2675 my $SuperClass = $ExceptionType{"SuperClass"};
2676 if($KnownRuntimeExceptions{$Exception}
2677 or defined $SuperClass and get_TypeName($SuperClass, 2) eq "java.lang.RuntimeException")
2678 {
2679 if(not $MethodInfo{1}{$Method}{"Abstract"}
2680 and not $MethodInfo{2}{$Method}{"Abstract"})
2681 {
2682 %{$CompatProblems{$Method}{"Added_Unchecked_Exception"}{"this.".get_SFormat($Exception)}} = (
2683 "Type_Name"=>$Class1{"Name"},
2684 "Target"=>$Exception );
2685 }
2686 }
2687 else
2688 {
2689 if($MethodInfo{1}{$Method}{"Abstract"}
2690 and $MethodInfo{2}{$Method}{"Abstract"})
2691 {
2692 %{$CompatProblems{$Method}{"Abstract_Method_Added_Checked_Exception"}{"this.".get_SFormat($Exception)}} = (
2693 "Type_Name"=>$Class1{"Name"},
2694 "Target"=>$Exception );
2695 }
2696 else
2697 {
2698 %{$CompatProblems{$Method}{"NonAbstract_Method_Added_Checked_Exception"}{"this.".get_SFormat($Exception)}} = (
2699 "Type_Name"=>$Class1{"Name"},
2700 "Target"=>$Exception );
2701 }
2702 }
2703 }
2704 }
2705
2706 if(defined $MethodInfo{1}{$Method}{"Param"})
2707 {
2708 foreach my $ParamPos (sort {int($a) <=> int($b)} keys(%{$MethodInfo{1}{$Method}{"Param"}}))
2709 { # checking parameters
2710 mergeParameters($Method, $ParamPos, $ParamPos);
2711 }
2712 }
2713
2714 # check object type
2715 my $ObjectType1_Id = $MethodInfo{1}{$Method}{"Class"};
2716 my $ObjectType2_Id = $MethodInfo{2}{$Method}{"Class"};
2717 if($ObjectType1_Id and $ObjectType2_Id)
2718 {
2719 my $SubProblems = mergeTypes($ObjectType1_Id, $ObjectType2_Id);
2720 foreach my $SubProblemType (keys(%{$SubProblems}))
2721 {
2722 foreach my $SubLocation (keys(%{$SubProblems->{$SubProblemType}}))
2723 {
2724 my $NewLocation = ($SubLocation)?"this.".$SubLocation:"this";
2725 $CompatProblems{$Method}{$SubProblemType}{$NewLocation} = $SubProblems->{$SubProblemType}{$SubLocation};
2726 }
2727 }
2728 }
2729 # check return type
2730 my $ReturnType1_Id = $MethodInfo{1}{$Method}{"Return"};
2731 my $ReturnType2_Id = $MethodInfo{2}{$Method}{"Return"};
2732 if($ReturnType1_Id and $ReturnType2_Id)
2733 {
2734 my $SubProblems = mergeTypes($ReturnType1_Id, $ReturnType2_Id);
2735 foreach my $SubProblemType (keys(%{$SubProblems}))
2736 {
2737 foreach my $SubLocation (keys(%{$SubProblems->{$SubProblemType}}))
2738 {
2739 my $NewLocation = ($SubLocation)?"retval.".$SubLocation:"retval";
2740 $CompatProblems{$Method}{$SubProblemType}{$NewLocation} = $SubProblems->{$SubProblemType}{$SubLocation};
2741 }
2742 }
2743 }
2744 }
2745}
2746
2747sub mergeParameters($$$)
2748{
2749 my ($Method, $ParamPos1, $ParamPos2) = @_;
2750 if(not $Method or not defined $MethodInfo{1}{$Method}{"Param"}
2751 or not defined $MethodInfo{2}{$Method}{"Param"}) {
2752 return;
2753 }
2754
2755 my $ParamType1_Id = $MethodInfo{1}{$Method}{"Param"}{$ParamPos1}{"Type"};
2756 my $ParamType2_Id = $MethodInfo{2}{$Method}{"Param"}{$ParamPos2}{"Type"};
2757
2758 if(not $ParamType1_Id or not $ParamType2_Id) {
2759 return;
2760 }
2761
2762 my $Parameter_Name = $MethodInfo{1}{$Method}{"Param"}{$ParamPos1}{"Name"};
2763 my $Parameter_Location = ($Parameter_Name)?$Parameter_Name:showPos($ParamPos1)." Parameter";
2764
2765 # checking type declaration changes
2766 my $SubProblems = mergeTypes($ParamType1_Id, $ParamType2_Id);
2767 foreach my $SubProblemType (keys(%{$SubProblems}))
2768 {
2769 foreach my $SubLocation (keys(%{$SubProblems->{$SubProblemType}}))
2770 {
2771 my $NewLocation = ($SubLocation)?$Parameter_Location.".".$SubLocation:$Parameter_Location;
2772 $CompatProblems{$Method}{$SubProblemType}{$NewLocation} = $SubProblems->{$SubProblemType}{$SubLocation};
2773 }
2774 }
2775}
2776
2777sub detectTypeChange($$$)
2778{
2779 my ($Type1_Id, $Type2_Id, $Prefix) = @_;
2780 my %LocalProblems = ();
2781 my %Type1 = get_Type($Type1_Id, 1);
2782 my %Type2 = get_Type($Type2_Id, 2);
2783 my %Type1_Base = ($Type1{"Type"} eq "array")?get_OneStep_BaseType($Type1_Id, 1):get_BaseType($Type1_Id, 1);
2784 my %Type2_Base = ($Type2{"Type"} eq "array")?get_OneStep_BaseType($Type2_Id, 2):get_BaseType($Type2_Id, 2);
2785 return () if(not $Type1{"Name"} or not $Type2{"Name"});
2786 return () if(not $Type1_Base{"Name"} or not $Type2_Base{"Name"});
2787 if($Type1_Base{"Name"} ne $Type2_Base{"Name"} and $Type1{"Name"} eq $Type2{"Name"})
2788 {# base type change
2789 %{$LocalProblems{"Changed_".$Prefix."_BaseType"}}=(
2790 "Old_Value"=>$Type1_Base{"Name"},
2791 "New_Value"=>$Type2_Base{"Name"} );
2792 }
2793 elsif($Type1{"Name"} ne $Type2{"Name"})
2794 {# type change
2795 %{$LocalProblems{"Changed_".$Prefix."_Type"}}=(
2796 "Old_Value"=>$Type1{"Name"},
2797 "New_Value"=>$Type2{"Name"} );
2798 }
2799 return %LocalProblems;
2800}
2801
2802sub htmlSpecChars($)
2803{
2804 my $Str = $_[0];
2805 if(not defined $Str
2806 or $Str eq "") {
2807 return "";
2808 }
2809 $Str=~s/\&([^#]|\Z)/&amp;$1/g;
2810 $Str=~s/</&lt;/g;
2811 $Str=~s/\-\>/&#45;&gt;/g; # &minus;
2812 $Str=~s/>/&gt;/g;
2813 $Str=~s/([^ ])( )([^ ])/$1\@ALONE_SP\@$3/g;
2814 $Str=~s/ /&#160;/g; # &nbsp;
2815 $Str=~s/\@ALONE_SP\@/ /g;
2816 $Str=~s/\n/<br\/>/g;
2817 $Str=~s/\"/&quot;/g;
2818 $Str=~s/\'/&#39;/g;
2819 return $Str;
2820}
2821
2822sub black_Name($$)
2823{
2824 my ($M, $V) = @_;
2825 return "<span class='iname_b'>".highLight_Signature($M, $V)."</span>";
2826}
2827
2828sub black_Name_S($)
2829{
2830 my $Name = $_[0];
2831 $Name=~s!\A(\w+)!<span class='iname_b'>$1</span>&#160;!g;
2832 return $Name;
2833}
2834
2835sub highLight_Signature($$)
2836{
2837 my ($M, $V) = @_;
2838 return get_Signature($M, $V, "Class|HTML|Italic");
2839}
2840
2841sub highLight_Signature_Italic_Color($$)
2842{
2843 my ($M, $V) = @_;
2844 return get_Signature($M, $V, "Full|HTML|Italic|Color");
2845}
2846
2847sub get_Signature($$$)
2848{
2849 my ($Method, $LibVersion, $Kind) = @_;
2850 if(defined $Cache{"get_Signature"}{$LibVersion}{$Method}{$Kind}) {
2851 return $Cache{"get_Signature"}{$LibVersion}{$Method}{$Kind};
2852 }
2853
2854 # settings
2855 my ($Full, $Html, $Simple, $Italic, $Color,
2856 $ShowParams, $ShowClass, $ShowAttr, $Target) = (0, 0, 0, 0, 0, 0, 0, 0, undef);
2857
2858 if($Kind=~/Full/) {
2859 $Full = 1;
2860 }
2861 if($Kind=~/HTML/) {
2862 $Html = 1;
2863 }
2864 if($Kind=~/Simple/) {
2865 $Simple = 1;
2866 }
2867 if($Kind=~/Italic/) {
2868 $Italic = 1;
2869 }
2870 if($Kind=~/Color/) {
2871 $Color = 1;
2872 }
2873 if($Kind=~/Target=(\d+)/) {
2874 $Target = $1;
2875 }
2876 if($Kind=~/Param/) {
2877 $ShowParams = 1;
2878 }
2879 if($Kind=~/Class/) {
2880 $ShowClass = 1;
2881 }
2882 if($Kind=~/Attr/) {
2883 $ShowAttr = 1;
2884 }
2885
2886 if(not defined $MethodInfo{$LibVersion}{$Method}{"ShortName"})
2887 { # from java.lang.Object
2888 if($Html or $Simple) {
2889 return htmlSpecChars($Method);
2890 }
2891 else
2892 {
2893 return $Method;
2894 }
2895 }
2896
2897 my $Signature = $MethodInfo{$LibVersion}{$Method}{"ShortName"};
2898 if($Full or $ShowClass)
2899 {
2900 my $Class = get_TypeName($MethodInfo{$LibVersion}{$Method}{"Class"}, $LibVersion);
2901
2902 if($HideTemplates) {
2903 $Class=~s/<.*>//g;
2904 }
2905
2906 if($Html) {
2907 $Class = htmlSpecChars($Class);
2908 }
2909
2910 $Signature = $Class.".".$Signature;
2911 }
2912 my @Params = ();
2913
2914 if(defined $MethodInfo{$LibVersion}{$Method}{"Param"})
2915 {
2916 foreach my $PPos (sort {int($a)<=>int($b)}
2917 keys(%{$MethodInfo{$LibVersion}{$Method}{"Param"}}))
2918 {
2919 my $PTid = $MethodInfo{$LibVersion}{$Method}{"Param"}{$PPos}{"Type"};
2920 if(my $PTName = get_TypeName($PTid, $LibVersion))
2921 {
2922 if($HideTemplates) {
2923 $PTName=~s/<.*>//g;
2924 }
2925
2926 if(not $ShowPackages) {
2927 $PTName=~s/(\A|\<\s*|\,\s*)[a-z0-9\.]+\./$1/g;
2928 }
2929
2930 if($Html) {
2931 $PTName = htmlSpecChars($PTName);
2932 }
2933
2934 if($Full or $ShowParams)
2935 {
2936 my $PName = $MethodInfo{$LibVersion}{$Method}{"Param"}{$PPos}{"Name"};
2937
2938 if($Simple) {
2939 $PName = "<i>$PName</i>";
2940 }
2941 elsif($Html)
2942 {
2943 my $Style = "param";
2944
2945 if(defined $Target
2946 and $Target==$PPos) {
2947 $PName = "<span class='focus_p'>$PName</span>";
2948 }
2949 elsif($Color) {
2950 $PName = "<span class='color_p'>$PName</span>";
2951 }
2952 else {
2953 $PName = "<i>$PName</i>";
2954 }
2955 }
2956
2957 push(@Params, $PTName." ".$PName);
2958 }
2959 else {
2960 push(@Params, $PTName);
2961 }
2962 }
2963 }
2964 }
2965
2966 if($Simple) {
2967 $Signature = "<b>".$Signature."</b>";
2968 }
2969
2970 if($Html and not $Simple)
2971 {
2972 $Signature .= "&#160;";
2973 $Signature .= "<span class='sym_p'>";
2974 if(@Params)
2975 {
2976 foreach my $Pos (0 .. $#Params)
2977 {
2978 my $Name = "";
2979
2980 if($Pos==0) {
2981 $Name .= "(&#160;";
2982 }
2983
2984 $Name .= $Params[$Pos];
2985
2986 $Name = "<span>".$Name."</span>";
2987
2988 if($Pos==$#Params) {
2989 $Name .= "&#160;)";
2990 }
2991 else {
2992 $Name .= ", ";
2993 }
2994
2995 $Signature .= $Name;
2996 }
2997 }
2998 else {
2999 $Signature .= "(&#160;)";
3000 }
3001 $Signature .= "</span>";
3002 }
3003 else
3004 {
3005 if(@Params) {
3006 $Signature .= " ( ".join(", ", @Params)." )";
3007 }
3008 else {
3009 $Signature .= " ( )";
3010 }
3011 }
3012
3013 if($Full or $ShowAttr)
3014 {
3015 if($MethodInfo{$LibVersion}{$Method}{"Static"}) {
3016 $Signature .= " [static]";
3017 }
3018 elsif($MethodInfo{$LibVersion}{$Method}{"Abstract"}) {
3019 $Signature .= " [abstract]";
3020 }
3021 }
3022
3023 if($Full)
3024 {
3025 if($ShowAccess)
3026 {
3027 if(my $Access = $MethodInfo{$LibVersion}{$Method}{"Access"})
3028 {
3029 if($Access ne "public") {
3030 $Signature .= " [".$Access."]";
3031 }
3032 }
3033 }
3034
3035 if(my $ReturnId = $MethodInfo{$LibVersion}{$Method}{"Return"})
3036 {
3037 my $RName = get_TypeName($ReturnId, $LibVersion);
3038
3039 if($HideTemplates) {
3040 $RName=~s/<.*>//g;
3041 }
3042
3043 if(not $ShowPackages) {
3044 $RName=~s/(\A|\<\s*|\,\s*)[a-z0-9\.]+\./$1/g;
3045 }
3046
3047 if($Simple) {
3048 $Signature .= " <b>:</b> ".htmlSpecChars($RName);
3049 }
3050 elsif($Html) {
3051 $Signature .= "<span class='sym_p nowrap'> &#160;<b>:</b>&#160;&#160;".htmlSpecChars($RName)."</span>";
3052 }
3053 else {
3054 $Signature .= " : ".$RName;
3055 }
3056 }
3057
3058 if(not $SkipDeprecated)
3059 {
3060 if($MethodInfo{$LibVersion}{$Method}{"Deprecated"}) {
3061 $Signature .= " *DEPRECATED*";
3062 }
3063 }
3064 }
3065
3066 $Signature=~s/java\.lang\.//g;
3067
3068 if($Html)
3069 {
3070 if(not $SkipDeprecated) {
3071 $Signature=~s!(\*deprecated\*)!<span class='deprecated'>$1</span>!ig;
3072 }
3073
3074 $Signature=~s!(\[static\]|\[abstract\]|\[public\]|\[private\]|\[protected\])!<span class='attr'>$1</span>!g;
3075 }
3076
3077 if($Simple) {
3078 $Signature=~s/\[\]/\[ \]/g;
3079 }
3080 elsif($Html)
3081 {
3082 $Signature=~s!\[\]![&#160;]!g;
3083 $Signature=~s!operator=!operator&#160;=!g;
3084 }
3085
3086 return ($Cache{"get_Signature"}{$LibVersion}{$Method}{$Kind} = $Signature);
3087}
3088
3089sub checkJavaCompiler($)
3090{ # check javac: compile simple program
3091 my $Cmd = $_[0];
3092
3093 if(not $Cmd) {
3094 return;
3095 }
3096
3097 writeFile($TMP_DIR."/test_javac/Simple.java",
3098 "public class Simple {
3099 public Integer f;
3100 public void method(Integer p) { };
3101 }");
3102 chdir($TMP_DIR."/test_javac");
3103 system("$Cmd Simple.java 2>errors.txt");
3104 chdir($ORIG_DIR);
3105 if($?)
3106 {
3107 my $Msg = "something is going wrong with the Java compiler (javac):\n";
3108 my $Err = readFile($TMP_DIR."/test_javac/errors.txt");
3109 $Msg .= $Err;
3110 if($Err=~/elf\/start\.S/ and $Err=~/undefined\s+reference\s+to/)
3111 { # /usr/lib/gcc/i586-suse-linux/4.5/../../../crt1.o: In function _start:
3112 # /usr/src/packages/BUILD/glibc-2.11.3/csu/../sysdeps/i386/elf/start.S:115: undefined reference to main
3113 $Msg .= "\nDid you install a JDK-devel package?";
3114 }
3115 exitStatus("Error", $Msg);
3116 }
3117}
3118
3119sub runTests($$$$)
3120{
3121 my ($TestsPath, $PackageName, $Path_v1, $Path_v2) = @_;
3122 # compile with old version of package
3123 my $JavacCmd = get_CmdPath("javac");
3124 if(not $JavacCmd) {
3125 exitStatus("Not_Found", "can't find \"javac\" compiler");
3126 }
3127 my $JavaCmd = get_CmdPath("java");
3128 if(not $JavaCmd) {
3129 exitStatus("Not_Found", "can't find \"java\" command");
3130 }
3131 mkpath($TestsPath."/$PackageName/");
3132 foreach my $ClassPath (cmd_find($Path_v1,"","*\.class",""))
3133 {# create a compile-time package copy
3134 copy($ClassPath, $TestsPath."/$PackageName/");
3135 }
3136 chdir($TestsPath);
3137 system($JavacCmd." -g *.java");
3138 chdir($ORIG_DIR);
3139 foreach my $TestSrc (cmd_find($TestsPath,"","*\.java",""))
3140 { # remove test source
3141 unlink($TestSrc);
3142 }
3143
3144 my $PkgPath = $TestsPath."/".$PackageName;
3145 if(-d $PkgPath) {
3146 rmtree($PkgPath);
3147 }
3148 mkpath($PkgPath);
3149 foreach my $ClassPath (cmd_find($Path_v2,"","*\.class",""))
3150 {# create a run-time package copy
3151 copy($ClassPath, $PkgPath."/");
3152 }
3153 my $TEST_REPORT = "";
3154 foreach my $TestPath (cmd_find($TestsPath,"","*\.class",1))
3155 {# run tests
3156 my $Name = get_filename($TestPath);
3157 $Name=~s/\.class\Z//g;
3158 chdir($TestsPath);
3159 system($JavaCmd." $Name >result.txt 2>&1");
3160 chdir($ORIG_DIR);
3161 my $Result = readFile($TestsPath."/result.txt");
3162 unlink($TestsPath."/result.txt");
3163 $TEST_REPORT .= "TEST CASE: $Name\n";
3164 if($Result) {
3165 $TEST_REPORT .= "RESULT: FAILED\n";
3166 $TEST_REPORT .= "OUTPUT:\n$Result\n";
3167 }
3168 else {
3169 $TEST_REPORT .= "RESULT: SUCCESS\n";
3170 }
3171 $TEST_REPORT .= "\n";
3172 }
3173 writeFile("$TestsPath/Journal.txt", $TEST_REPORT);
3174
3175 if(-d $PkgPath) {
3176 rmtree($PkgPath);
3177 }
3178}
3179
3180sub compileJavaLib($$$)
3181{
3182 my ($LibName, $BuildRoot1, $BuildRoot2) = @_;
3183 my $JavacCmd = get_CmdPath("javac");
3184 if(not $JavacCmd) {
3185 exitStatus("Not_Found", "can't find \"javac\" compiler");
3186 }
3187 checkJavaCompiler($JavacCmd);
3188 my $JarCmd = get_CmdPath("jar");
3189 if(not $JarCmd) {
3190 exitStatus("Not_Found", "can't find \"jar\" command");
3191 }
3192 writeFile("$BuildRoot1/MANIFEST.MF", "Implementation-Version: 1.0\n");
3193 # space before value, new line
3194 writeFile("$BuildRoot2/MANIFEST.MF", "Implementation-Version: 2.0\n");
3195 my (%SrcDir1, %SrcDir2) = ();
3196 foreach my $Path (cmd_find($BuildRoot1,"f","*.java","")) {
3197 $SrcDir1{get_dirname($Path)} = 1;
3198 }
3199 foreach my $Path (cmd_find($BuildRoot2,"f","*.java","")) {
3200 $SrcDir2{get_dirname($Path)} = 1;
3201 }
3202 # build classes v.1
3203 foreach my $Dir (keys(%SrcDir1))
3204 {
3205 chdir($Dir);
3206 system("$JavacCmd -g *.java");
3207 chdir($ORIG_DIR);
3208 if($?) {
3209 exitStatus("Error", "can't compile classes v.1");
3210 }
3211 }
3212 # create java archive v.1
3213 chdir($BuildRoot1);
3214 system("$JarCmd -cmf MANIFEST.MF $LibName.jar TestPackage");
3215 chdir($ORIG_DIR);
3216
3217 # build classes v.2
3218 foreach my $Dir (keys(%SrcDir2))
3219 {
3220 chdir($Dir);
3221 system("$JavacCmd -g *.java");
3222 chdir($ORIG_DIR);
3223 if($?) {
3224 exitStatus("Error", "can't compile classes v.2");
3225 }
3226 }
3227 # create java archive v.2
3228 chdir($BuildRoot2);
3229 system("$JarCmd -cmf MANIFEST.MF $LibName.jar TestPackage");
3230 chdir($ORIG_DIR);
3231
3232 foreach my $SrcPath (cmd_find($BuildRoot1,"","*\.java","")) {
3233 unlink($SrcPath);
3234 }
3235 foreach my $SrcPath (cmd_find($BuildRoot2,"","*\.java","")) {
3236 unlink($SrcPath);
3237 }
3238 return 1;
3239}
3240
3241sub readLineNum($$)
3242{
3243 my ($Path, $Num) = @_;
3244 return "" if(not $Path or not -f $Path);
3245 open (FILE, $Path);
3246 foreach (1 ... $Num) {
3247 <FILE>;
3248 }
3249 my $Line = <FILE>;
3250 close(FILE);
3251 return $Line;
3252}
3253
3254sub readAttributes($$)
3255{
3256 my ($Path, $Num) = @_;
3257 return () if(not $Path or not -f $Path);
3258 my %Attributes = ();
3259 if(readLineNum($Path, $Num)=~/<!--\s+(.+)\s+-->/)
3260 {
3261 foreach my $AttrVal (split(/;/, $1))
3262 {
3263 if($AttrVal=~/(.+):(.+)/)
3264 {
3265 my ($Name, $Value) = ($1, $2);
3266 $Attributes{$Name} = $Value;
3267 }
3268 }
3269 }
3270 return \%Attributes;
3271}
3272
3273sub runChecker($$$)
3274{
3275 my ($LibName, $Path1, $Path2) = @_;
3276 writeFile("$LibName/v1.xml", "
3277 <version>
3278 1.0
3279 </version>
3280 <archives>
3281 ".get_abs_path($Path1)."
3282 </archives>");
3283 writeFile("$LibName/v2.xml", "
3284 <version>
3285 2.0
3286 </version>
3287 <archives>
3288 ".get_abs_path($Path2)."
3289 </archives>");
3290 my $Cmd = "perl $0 -l $LibName $LibName/v1.xml $LibName/v2.xml";
3291 if($Quick) {
3292 $Cmd .= " -quick";
3293 }
3294 if(defined $SkipDeprecated) {
3295 $Cmd .= " -skip-deprecated";
3296 }
3297 if(defined $OldStyle) {
3298 $Cmd .= " -old-style";
3299 }
3300 writeFile($TMP_DIR."/skip-annotations.list", "TestPackage.Beta");
3301 $Cmd .= " -skip-annotations-list ".$TMP_DIR."/skip-annotations.list";
3302 if($Debug)
3303 {
3304 $Cmd .= " -debug";
3305 printMsg("INFO", "running $Cmd");
3306 }
3307 system($Cmd);
3308 my $Report = "compat_reports/$LibName/1.0_to_2.0/compat_report.html";
3309 # Binary
3310 my $BReport = readAttributes($Report, 0);
3311 my $NProblems = $BReport->{"type_problems_high"}+$BReport->{"type_problems_medium"};
3312 $NProblems += $BReport->{"method_problems_high"}+$BReport->{"method_problems_medium"};
3313 $NProblems += $BReport->{"removed"};
3314 # Source
3315 my $SReport = readAttributes($Report, 1);
3316 $NProblems += $SReport->{"type_problems_high"}+$SReport->{"type_problems_medium"};
3317 $NProblems += $SReport->{"method_problems_high"}+$SReport->{"method_problems_medium"};
3318 $NProblems += $SReport->{"removed"};
3319 if($NProblems>=100) {
3320 printMsg("INFO", "test result: SUCCESS ($NProblems breaks found)\n");
3321 }
3322 else {
3323 printMsg("ERROR", "test result: FAILED ($NProblems breaks found)\n");
3324 }
3325}
3326
3327sub writeFile($$)
3328{
3329 my ($Path, $Content) = @_;
3330 return if(not $Path);
3331 if(my $Dir = get_dirname($Path)) {
3332 mkpath($Dir);
3333 }
3334 open (FILE, ">".$Path) || die ("can't open file \'$Path\': $!\n");
3335 print FILE $Content;
3336 close(FILE);
3337}
3338
3339sub readFile($)
3340{
3341 my $Path = $_[0];
3342 return "" if(not $Path or not -f $Path);
3343 open (FILE, $Path);
3344 my $Content = join("", <FILE>);
3345 close(FILE);
3346 $Content=~s/\r//g;
3347 return $Content;
3348}
3349
3350sub appendFile($$)
3351{
3352 my ($Path, $Content) = @_;
3353 return if(not $Path);
3354 if(my $Dir = get_dirname($Path)) {
3355 mkpath($Dir);
3356 }
3357 open(FILE, ">>".$Path) || die ("can't open file \'$Path\': $!\n");
3358 print FILE $Content;
3359 close(FILE);
3360}
3361
3362sub get_Report_Header($)
3363{
3364 my $Level = $_[0];
3365 my $Report_Header = "<h1>";
3366 if($Level eq "Source") {
3367 $Report_Header .= "Source compatibility";
3368 }
3369 elsif($Level eq "Binary") {
3370 $Report_Header .= "Binary compatibility";
3371 }
3372 else {
3373 $Report_Header .= "API compatibility";
3374 }
3375 $Report_Header .= " report for the <span style='color:Blue;'>$TargetTitle</span> library between <span style='color:Red;'>".$Descriptor{1}{"Version"}."</span> and <span style='color:Red;'>".$Descriptor{2}{"Version"}."</span> versions";
3376 if($ClientPath) {
3377 $Report_Header .= " (concerning portability of the client: <span style='color:Blue;'>".get_filename($ClientPath)."</span>)";
3378 }
3379 $Report_Header .= "</h1>\n";
3380 return $Report_Header;
3381}
3382
3383sub get_SourceInfo()
3384{
3385 my $CheckedArchives = "<a name='Checked_Archives'></a>";
3386 if($OldStyle) {
3387 $CheckedArchives .= "<h2>Java ARchives (".keys(%{$LibArchives{1}}).")</h2>";
3388 }
3389 else {
3390 $CheckedArchives .= "<h2>Java ARchives <span class='gray'>&nbsp;".keys(%{$LibArchives{1}})."&nbsp;</span></h2>";
3391 }
3392 $CheckedArchives .= "\n<hr/><div class='jar_list'>\n";
3393 foreach my $ArchivePath (sort {lc($a) cmp lc($b)} keys(%{$LibArchives{1}})) {
3394 $CheckedArchives .= get_filename($ArchivePath)."<br/>\n";
3395 }
3396 $CheckedArchives .= "</div><br/>$TOP_REF<br/>\n";
3397 return $CheckedArchives;
3398}
3399
3400sub get_TypeProblems_Count($$)
3401{
3402 my ($TargetSeverity, $Level) = @_;
3403 my $Type_Problems_Count = 0;
3404
3405 foreach my $Type_Name (sort keys(%{$TypeChanges{$Level}}))
3406 {
3407 my %Kinds_Target = ();
3408 foreach my $Kind (sort keys(%{$TypeChanges{$Level}{$Type_Name}}))
3409 {
3410 if($TypeProblems_Kind{$Level}{$Kind} ne $TargetSeverity) {
3411 next;
3412 }
3413 foreach my $Location (sort keys(%{$TypeChanges{$Level}{$Type_Name}{$Kind}}))
3414 {
3415 my $Target = $TypeChanges{$Level}{$Type_Name}{$Kind}{$Location}{"Target"};
3416
3417 if($Kinds_Target{$Kind}{$Target}) {
3418 next;
3419 }
3420
3421 $Kinds_Target{$Kind}{$Target} = 1;
3422 $Type_Problems_Count += 1;
3423 }
3424 }
3425 }
3426
3427 return $Type_Problems_Count;
3428}
3429
3430sub show_number($)
3431{
3432 if($_[0])
3433 {
3434 my $Num = cut_off_number($_[0], 2, 0);
3435 if($Num eq "0")
3436 {
3437 foreach my $P (3 .. 7)
3438 {
3439 $Num = cut_off_number($_[0], $P, 1);
3440 if($Num ne "0") {
3441 last;
3442 }
3443 }
3444 }
3445 if($Num eq "0") {
3446 $Num = $_[0];
3447 }
3448 return $Num;
3449 }
3450 return $_[0];
3451}
3452
3453sub cut_off_number($$$)
3454{
3455 my ($num, $digs_to_cut, $z) = @_;
3456 if($num!~/\./)
3457 {
3458 $num .= ".";
3459 foreach (1 .. $digs_to_cut-1) {
3460 $num .= "0";
3461 }
3462 }
3463 elsif($num=~/\.(.+)\Z/ and length($1)<$digs_to_cut-1)
3464 {
3465 foreach (1 .. $digs_to_cut - 1 - length($1)) {
3466 $num .= "0";
3467 }
3468 }
3469 elsif($num=~/\d+\.(\d){$digs_to_cut,}/) {
3470 $num=sprintf("%.".($digs_to_cut-1)."f", $num);
3471 }
3472 $num=~s/\.[0]+\Z//g;
3473 if($z) {
3474 $num=~s/(\.[1-9]+)[0]+\Z/$1/g;
3475 }
3476 return $num;
3477}
3478
3479sub get_Summary($)
3480{
3481 my $Level = $_[0];
3482 my ($Added, $Removed, $M_Problems_High, $M_Problems_Medium, $M_Problems_Low,
3483 $T_Problems_High, $T_Problems_Medium, $T_Problems_Low, $M_Other, $T_Other) = (0,0,0,0,0,0,0,0,0,0);
3484
3485 %{$RESULT{$Level}} = (
3486 "Problems"=>0,
3487 "Warnings"=>0,
3488 "Affected"=>0 );
3489
3490 foreach my $Method (sort keys(%CompatProblems))
3491 {
3492 foreach my $Kind (sort keys(%{$CompatProblems{$Method}}))
3493 {
3494 if(my $Severity = $MethodProblems_Kind{$Level}{$Kind})
3495 {
3496 foreach my $Location (sort keys(%{$CompatProblems{$Method}{$Kind}}))
3497 {
3498 if($Kind eq "Added_Method")
3499 {
3500 if($Level eq "Source")
3501 {
3502 if($ChangedReturnFromVoid{$Method}) {
3503 next;
3504 }
3505 }
3506 $Added+=1;
3507 }
3508 elsif($Kind eq "Removed_Method")
3509 {
3510 if($Level eq "Source")
3511 {
3512 if($ChangedReturnFromVoid{$Method}) {
3513 next;
3514 }
3515 }
3516 $Removed+=1;
3517 $TotalAffected{$Level}{$Method} = $Severity;
3518 }
3519 else
3520 {
3521 if($Severity eq "Safe") {
3522 $M_Other += 1;
3523 }
3524 elsif($Severity eq "High") {
3525 $M_Problems_High+=1;
3526 }
3527 elsif($Severity eq "Medium") {
3528 $M_Problems_Medium+=1;
3529 }
3530 elsif($Severity eq "Low") {
3531 $M_Problems_Low+=1;
3532 }
3533 if(($Severity ne "Low" or $StrictCompat)
3534 and $Severity ne "Safe") {
3535 $TotalAffected{$Level}{$Method} = $Severity;
3536 }
3537 }
3538 }
3539 }
3540 }
3541 }
3542
3543 my %MethodTypeIndex = ();
3544
3545 foreach my $Method (sort keys(%CompatProblems))
3546 {
3547 my @Kinds = sort keys(%{$CompatProblems{$Method}});
3548 foreach my $Kind (@Kinds)
3549 {
3550 if(my $Severity = $TypeProblems_Kind{$Level}{$Kind})
3551 {
3552 my @Locs = sort {length($a)<=>length($b)} sort keys(%{$CompatProblems{$Method}{$Kind}});
3553 foreach my $Location (@Locs)
3554 {
3555 my $Type_Name = $CompatProblems{$Method}{$Kind}{$Location}{"Type_Name"};
3556 my $Target = $CompatProblems{$Method}{$Kind}{$Location}{"Target"};
3557
3558 if(defined $MethodTypeIndex{$Method}{$Type_Name}{$Kind}{$Target})
3559 { # one location for one type and target
3560 next;
3561 }
3562 $MethodTypeIndex{$Method}{$Type_Name}{$Kind}{$Target} = 1;
3563 $TypeChanges{$Level}{$Type_Name}{$Kind}{$Location} = $CompatProblems{$Method}{$Kind}{$Location};
3564
3565 if(($Severity ne "Low" or $StrictCompat)
3566 and $Severity ne "Safe")
3567 {
3568 if(my $Sev = $TotalAffected{$Level}{$Method})
3569 {
3570 if($Severity_Val{$Severity}>$Severity_Val{$Sev}) {
3571 $TotalAffected{$Level}{$Method} = $Severity;
3572 }
3573 }
3574 else {
3575 $TotalAffected{$Level}{$Method} = $Severity;
3576 }
3577 }
3578 }
3579 }
3580 }
3581 }
3582
3583 %MethodTypeIndex = (); # clear memory
3584
3585
3586 $T_Problems_High = get_TypeProblems_Count("High", $Level);
3587 $T_Problems_Medium = get_TypeProblems_Count("Medium", $Level);
3588 $T_Problems_Low = get_TypeProblems_Count("Low", $Level);
3589 $T_Other = get_TypeProblems_Count("Safe", $Level);
3590
3591 my $SCount = keys(%CheckedMethods)-$Added;
3592 if($SCount)
3593 {
3594 my %Weight = (
3595 "High" => 100,
3596 "Medium" => 50,
3597 "Low" => 25
3598 );
3599 foreach (keys(%{$TotalAffected{$Level}})) {
3600 $RESULT{$Level}{"Affected"}+=$Weight{$TotalAffected{$Level}{$_}};
3601 }
3602 $RESULT{$Level}{"Affected"} = $RESULT{$Level}{"Affected"}/$SCount;
3603 }
3604 else {
3605 $RESULT{$Level}{"Affected"} = 0;
3606 }
3607 $RESULT{$Level}{"Affected"} = show_number($RESULT{$Level}{"Affected"});
3608 if($RESULT{$Level}{"Affected"}>=100) {
3609 $RESULT{$Level}{"Affected"} = 100;
3610 }
3611
3612 my ($TestInfo, $TestResults, $Problem_Summary) = ();
3613
3614 # test info
3615 $TestInfo .= "<h2>Test Info</h2><hr/>\n";
3616 $TestInfo .= "<table class='summary'>\n";
3617 $TestInfo .= "<tr><th>Library Name</th><td>$TargetTitle</td></tr>\n";
3618 $TestInfo .= "<tr><th>Version #1</th><td>".$Descriptor{1}{"Version"}."</td></tr>\n";
3619 $TestInfo .= "<tr><th>Version #2</th><td>".$Descriptor{2}{"Version"}."</td></tr>\n";
3620
3621 if($JoinReport)
3622 {
3623 if($Level eq "Binary") {
3624 $TestInfo .= "<tr><th>Subject</th><td width='150px'>Binary Compatibility</td></tr>\n"; # Run-time
3625 }
3626 if($Level eq "Source") {
3627 $TestInfo .= "<tr><th>Subject</th><td width='150px'>Source Compatibility</td></tr>\n"; # Build-time
3628 }
3629 }
3630 $TestInfo .= "</table>\n";
3631
3632 # test results
3633 $TestResults .= "<h2>Test Results</h2><hr/>";
3634 $TestResults .= "<table class='summary'>";
3635
3636 my $Checked_Archives_Link = "0";
3637 $Checked_Archives_Link = "<a href='#Checked_Archives' style='color:Blue;'>".keys(%{$LibArchives{1}})."</a>" if(keys(%{$LibArchives{1}})>0);
3638
3639 $TestResults .= "<tr><th>Total JARs</th><td>$Checked_Archives_Link</td></tr>\n";
3640 $TestResults .= "<tr><th>Total Methods / Classes</th><td>".keys(%CheckedMethods)." / ".keys(%CheckedTypes)."</td></tr>\n";
3641
3642 $RESULT{$Level}{"Problems"} += $Removed+$M_Problems_High+$T_Problems_High+$T_Problems_Medium+$M_Problems_Medium;
3643 if($StrictCompat) {
3644 $RESULT{$Level}{"Problems"}+=$T_Problems_Low+$M_Problems_Low;
3645 }
3646 else {
3647 $RESULT{$Level}{"Warnings"}+=$T_Problems_Low+$M_Problems_Low;
3648 }
3649
3650 my $META_DATA = "kind:".lc($Level).";";
3651 $META_DATA .= $RESULT{$Level}{"Problems"}?"verdict:incompatible;":"verdict:compatible;";
3652 $TestResults .= "<tr><th>Compatibility</th>\n";
3653
3654 my $BC_Rate = show_number(100 - $RESULT{$Level}{"Affected"});
3655
3656 if($RESULT{$Level}{"Problems"})
3657 {
3658 my $Cl = "incompatible";
3659 if($BC_Rate>=90) {
3660 $Cl = "warning";
3661 }
3662 elsif($BC_Rate>=80) {
3663 $Cl = "almost_compatible";
3664 }
3665
3666 $TestResults .= "<td class=\'$Cl\'>".$BC_Rate."%</td>\n";
3667 }
3668 else
3669 {
3670 $TestResults .= "<td class=\'compatible\'>100%</td>\n";
3671 }
3672
3673 $TestResults .= "</tr>\n";
3674 $TestResults .= "</table>\n";
3675
3676 $META_DATA .= "affected:".$RESULT{$Level}{"Affected"}.";";# in percents
3677
3678 # Problem Summary
3679 $Problem_Summary .= "<h2>Problem Summary</h2><hr/>";
3680 $Problem_Summary .= "<table class='summary'>";
3681 $Problem_Summary .= "<tr><th></th><th style='text-align:center;'>Severity</th><th style='text-align:center;'>Count</th></tr>";
3682
3683 my $Added_Link = "0";
3684 if($Added>0)
3685 {
3686 if($ShortMode) {
3687 $Added_Link = $Added;
3688 }
3689 else
3690 {
3691 if($JoinReport) {
3692 $Added_Link = "<a href='#".$Level."_Added' style='color:Blue;'>$Added</a>";
3693 }
3694 else {
3695 $Added_Link = "<a href='#Added' style='color:Blue;'>$Added</a>";
3696 }
3697 }
3698 }
3699 $META_DATA .= "added:$Added;";
3700 $Problem_Summary .= "<tr><th>Added Methods</th><td>-</td><td".getStyle("M", "Added", $Added).">$Added_Link</td></tr>";
3701
3702 my $Removed_Link = "0";
3703 if($Removed>0)
3704 {
3705 if($ShortMode) {
3706 $Removed_Link = $Removed;
3707 }
3708 else
3709 {
3710 if($JoinReport) {
3711 $Removed_Link = "<a href='#".$Level."_Removed' style='color:Blue;'>$Removed</a>"
3712 }
3713 else {
3714 $Removed_Link = "<a href='#Removed' style='color:Blue;'>$Removed</a>"
3715 }
3716 }
3717 }
3718 $META_DATA .= "removed:$Removed;";
3719 $Problem_Summary .= "<tr><th>Removed Methods</th>";
3720 $Problem_Summary .= "<td>High</td><td".getStyle("M", "Removed", $Removed).">$Removed_Link</td></tr>";
3721
3722 my $TH_Link = "0";
3723 $TH_Link = "<a href='#".get_Anchor("Type", $Level, "High")."' style='color:Blue;'>$T_Problems_High</a>" if($T_Problems_High>0);
3724 $META_DATA .= "type_problems_high:$T_Problems_High;";
3725 $Problem_Summary .= "<tr><th rowspan='3'>Problems with<br/>Data Types</th>";
3726 $Problem_Summary .= "<td>High</td><td".getStyle("T", "High", $T_Problems_High).">$TH_Link</td></tr>";
3727
3728 my $TM_Link = "0";
3729 $TM_Link = "<a href='#".get_Anchor("Type", $Level, "Medium")."' style='color:Blue;'>$T_Problems_Medium</a>" if($T_Problems_Medium>0);
3730 $META_DATA .= "type_problems_medium:$T_Problems_Medium;";
3731 $Problem_Summary .= "<tr><td>Medium</td><td".getStyle("T", "Medium", $T_Problems_Medium).">$TM_Link</td></tr>";
3732
3733 my $TL_Link = "0";
3734 $TL_Link = "<a href='#".get_Anchor("Type", $Level, "Low")."' style='color:Blue;'>$T_Problems_Low</a>" if($T_Problems_Low>0);
3735 $META_DATA .= "type_problems_low:$T_Problems_Low;";
3736 $Problem_Summary .= "<tr><td>Low</td><td".getStyle("T", "Low", $T_Problems_Low).">$TL_Link</td></tr>";
3737
3738 my $MH_Link = "0";
3739 $MH_Link = "<a href='#".get_Anchor("Method", $Level, "High")."' style='color:Blue;'>$M_Problems_High</a>" if($M_Problems_High>0);
3740 $META_DATA .= "method_problems_high:$M_Problems_High;";
3741 $Problem_Summary .= "<tr><th rowspan='3'>Problems with<br/>Methods</th>";
3742 $Problem_Summary .= "<td>High</td><td".getStyle("M", "High", $M_Problems_High).">$MH_Link</td></tr>";
3743
3744 my $MM_Link = "0";
3745 $MM_Link = "<a href='#".get_Anchor("Method", $Level, "Medium")."' style='color:Blue;'>$M_Problems_Medium</a>" if($M_Problems_Medium>0);
3746 $META_DATA .= "method_problems_medium:$M_Problems_Medium;";
3747 $Problem_Summary .= "<tr><td>Medium</td><td".getStyle("M", "Medium", $M_Problems_Medium).">$MM_Link</td></tr>";
3748
3749 my $ML_Link = "0";
3750 $ML_Link = "<a href='#".get_Anchor("Method", $Level, "Low")."' style='color:Blue;'>$M_Problems_Low</a>" if($M_Problems_Low>0);
3751 $META_DATA .= "method_problems_low:$M_Problems_Low;";
3752 $Problem_Summary .= "<tr><td>Low</td><td".getStyle("M", "Low", $M_Problems_Low).">$ML_Link</td></tr>";
3753
3754 # Safe Changes
3755 if($T_Other)
3756 {
3757 my $TS_Link = "<a href='#".get_Anchor("Type", $Level, "Safe")."' style='color:Blue;'>$T_Other</a>";
3758 $Problem_Summary .= "<tr><th>Other Changes<br/>in Data Types</th><td>-</td><td".getStyle("T", "Safe", $T_Other).">$TS_Link</td></tr>\n";
3759 }
3760
3761 if($M_Other)
3762 {
3763 my $MS_Link = "<a href='#".get_Anchor("Method", $Level, "Safe")."' style='color:Blue;'>$M_Other</a>";
3764 $Problem_Summary .= "<tr><th>Other Changes<br/>in Methods</th><td>-</td><td".getStyle("M", "Safe", $M_Other).">$MS_Link</td></tr>\n";
3765 }
3766 $META_DATA .= "checked_methods:".keys(%CheckedMethods).";";
3767 $META_DATA .= "checked_types:".keys(%CheckedTypes).";";
3768 $META_DATA .= "tool_version:$TOOL_VERSION";
3769 $Problem_Summary .= "</table>\n";
3770 return ($TestInfo.$TestResults.$Problem_Summary, $META_DATA);
3771}
3772
3773sub getStyle($$$)
3774{
3775 my ($Subj, $Act, $Num) = @_;
3776 my %Style = (
3777 "Added"=>"new",
3778 "Removed"=>"failed",
3779 "Safe"=>"passed",
3780 "Low"=>"warning",
3781 "Medium"=>"failed",
3782 "High"=>"failed"
3783 );
3784
3785 if($Num>0) {
3786 return " class='".$Style{$Act}."'";
3787 }
3788
3789 return "";
3790}
3791
3792sub get_Anchor($$$)
3793{
3794 my ($Kind, $Level, $Severity) = @_;
3795 if($JoinReport)
3796 {
3797 if($Severity eq "Safe") {
3798 return "Other_".$Level."_Changes_In_".$Kind."s";
3799 }
3800 else {
3801 return $Kind."_".$Level."_Problems_".$Severity;
3802 }
3803 }
3804 else
3805 {
3806 if($Severity eq "Safe") {
3807 return "Other_Changes_In_".$Kind."s";
3808 }
3809 else {
3810 return $Kind."_Problems_".$Severity;
3811 }
3812 }
3813}
3814
3815sub get_Report_Added($)
3816{
3817 if($ShortMode) {
3818 return "";
3819 }
3820
3821 my $Level = $_[0];
3822 my ($ADDED_METHODS, %MethodAddedInArchiveClass);
3823 foreach my $Method (sort keys(%CompatProblems))
3824 {
3825 foreach my $Kind (sort keys(%{$CompatProblems{$Method}}))
3826 {
3827 if($Kind eq "Added_Method")
3828 {
3829 my $ArchiveName = $MethodInfo{2}{$Method}{"Archive"};
3830 my $ClassName = get_ShortName($MethodInfo{2}{$Method}{"Class"}, 2);
3831 if($Level eq "Source")
3832 {
3833 if($ChangedReturnFromVoid{$Method}) {
3834 next;
3835 }
3836 }
3837 $MethodAddedInArchiveClass{$ArchiveName}{$ClassName}{$Method} = 1;
3838 }
3839 }
3840 }
3841 my $Added_Number = 0;
3842 foreach my $ArchiveName (sort {lc($a) cmp lc($b)} keys(%MethodAddedInArchiveClass))
3843 {
3844 foreach my $ClassName (sort {lc($a) cmp lc($b)} keys(%{$MethodAddedInArchiveClass{$ArchiveName}}))
3845 {
3846 my %NameSpace_Method = ();
3847 foreach my $Method (keys(%{$MethodAddedInArchiveClass{$ArchiveName}{$ClassName}})) {
3848 $NameSpace_Method{$MethodInfo{2}{$Method}{"Package"}}{$Method} = 1;
3849 }
3850
3851 my $ShowClass = $ClassName;
3852 $ShowClass=~s/<.*>//g;
3853
3854 foreach my $NameSpace (sort keys(%NameSpace_Method))
3855 {
3856 $ADDED_METHODS .= "<span class='jar'>$ArchiveName</span>, <span class='cname'>".htmlSpecChars($ShowClass).".class</span><br/>\n";
3857
3858 if($NameSpace) {
3859 $ADDED_METHODS .= "<span class='pkg_t'>package</span> <span class='pkg'>$NameSpace</span><br/>\n";
3860 }
3861
3862 if($Compact) {
3863 $ADDED_METHODS .= "<div class='symbols'>";
3864 }
3865
3866 my @SortedMethods = sort {lc($MethodInfo{2}{$a}{"Signature"}) cmp lc($MethodInfo{2}{$b}{"Signature"})} keys(%{$NameSpace_Method{$NameSpace}});
3867 foreach my $Method (@SortedMethods)
3868 {
3869 $Added_Number += 1;
3870
3871 my $Signature = undef;
3872
3873 if($Compact) {
3874 $Signature = get_Signature($Method, 2, "Full|HTML|Simple");
3875 }
3876 else {
3877 $Signature = highLight_Signature_Italic_Color($Method, 2);
3878 }
3879
3880 if($NameSpace) {
3881 $Signature=~s/(\W|\A)\Q$NameSpace\E\.(\w)/$1$2/g;
3882 }
3883
3884 if($Compact) {
3885 $ADDED_METHODS .= "&nbsp;".$Signature."<br/>\n";
3886 }
3887 else {
3888 $ADDED_METHODS .= insertIDs($ContentSpanStart.$Signature.$ContentSpanEnd."<br/>\n".$ContentDivStart."<span class='mngl'>[mangled: <b>".htmlSpecChars($Method)."</b>]</span><br/><br/>".$ContentDivEnd."\n");
3889 }
3890 }
3891
3892 if($Compact) {
3893 $ADDED_METHODS .= "</div>";
3894 }
3895
3896 $ADDED_METHODS .= "<br/>\n";
3897 }
3898
3899 }
3900 }
3901 if($ADDED_METHODS)
3902 {
3903 my $Anchor = "<a name='Added'></a>";
3904 if($JoinReport) {
3905 $Anchor = "<a name='".$Level."_Added'></a>";
3906 }
3907 if($OldStyle) {
3908 $ADDED_METHODS = "<h2>Added Methods ($Added_Number)</h2><hr/>\n".$ADDED_METHODS;
3909 }
3910 else {
3911 $ADDED_METHODS = "<h2>Added Methods <span".getStyle("M", "Added", $Added_Number).">&nbsp;$Added_Number&nbsp;</span></h2><hr/>\n".$ADDED_METHODS;
3912 }
3913 $ADDED_METHODS = $Anchor.$ADDED_METHODS.$TOP_REF."<br/>\n";
3914 }
3915 return $ADDED_METHODS;
3916}
3917
3918sub get_Report_Removed($)
3919{
3920 if($ShortMode) {
3921 return "";
3922 }
3923
3924 my $Level = $_[0];
3925 my ($REMOVED_METHODS, %MethodRemovedFromArchiveClass);
3926 foreach my $Method (sort keys(%CompatProblems))
3927 {
3928 foreach my $Kind (sort keys(%{$CompatProblems{$Method}}))
3929 {
3930 if($Kind eq "Removed_Method")
3931 {
3932 if($Level eq "Source")
3933 {
3934 if($ChangedReturnFromVoid{$Method}) {
3935 next;
3936 }
3937 }
3938 my $ArchiveName = $MethodInfo{1}{$Method}{"Archive"};
3939 my $ClassName = get_ShortName($MethodInfo{1}{$Method}{"Class"}, 1);
3940 $MethodRemovedFromArchiveClass{$ArchiveName}{$ClassName}{$Method} = 1;
3941 }
3942 }
3943 }
3944 my $Removed_Number = 0;
3945 foreach my $ArchiveName (sort {lc($a) cmp lc($b)} keys(%MethodRemovedFromArchiveClass))
3946 {
3947 foreach my $ClassName (sort {lc($a) cmp lc($b)} keys(%{$MethodRemovedFromArchiveClass{$ArchiveName}}))
3948 {
3949 my %NameSpace_Method = ();
3950 foreach my $Method (keys(%{$MethodRemovedFromArchiveClass{$ArchiveName}{$ClassName}}))
3951 {
3952 $NameSpace_Method{$MethodInfo{1}{$Method}{"Package"}}{$Method} = 1;
3953 }
3954
3955 my $ShowClass = $ClassName;
3956 $ShowClass=~s/<.*>//g;
3957
3958 foreach my $NameSpace (sort keys(%NameSpace_Method))
3959 {
3960 $REMOVED_METHODS .= "<span class='jar'>$ArchiveName</span>, <span class='cname'>".htmlSpecChars($ShowClass).".class</span><br/>\n";
3961
3962 if($NameSpace) {
3963 $REMOVED_METHODS .= "<span class='pkg_t'>package</span> <span class='pkg'>$NameSpace</span><br/>\n";
3964 }
3965
3966 if($Compact) {
3967 $REMOVED_METHODS .= "<div class='symbols'>";
3968 }
3969
3970 my @SortedMethods = sort {lc($MethodInfo{1}{$a}{"Signature"}) cmp lc($MethodInfo{1}{$b}{"Signature"})} keys(%{$NameSpace_Method{$NameSpace}});
3971 foreach my $Method (@SortedMethods)
3972 {
3973 $Removed_Number += 1;
3974
3975 my $Signature = undef;
3976
3977 if($Compact) {
3978 $Signature = get_Signature($Method, 1, "Full|HTML|Simple");
3979 }
3980 else {
3981 $Signature = highLight_Signature_Italic_Color($Method, 1);
3982 }
3983
3984 if($NameSpace) {
3985 $Signature=~s/(\W|\A)\Q$NameSpace\E\.(\w)/$1$2/g;
3986 }
3987
3988 if($Compact) {
3989 $REMOVED_METHODS .= "&nbsp;".$Signature."<br/>\n";
3990 }
3991 else {
3992 $REMOVED_METHODS .= insertIDs($ContentSpanStart.$Signature.$ContentSpanEnd."<br/>\n".$ContentDivStart."<span class='mngl'>[mangled: <b>".htmlSpecChars($Method)."</b>]</span><br/><br/>".$ContentDivEnd."\n");
3993 }
3994 }
3995
3996 if($Compact) {
3997 $REMOVED_METHODS .= "</div>";
3998 }
3999
4000 $REMOVED_METHODS .= "<br/>\n";
4001 }
4002 }
4003 }
4004 if($REMOVED_METHODS)
4005 {
4006 my $Anchor = "<a name='Removed'></a><a name='Withdrawn'></a>";
4007 if($JoinReport) {
4008 $Anchor = "<a name='".$Level."_Removed'></a><a name='".$Level."_Withdrawn'></a>";
4009 }
4010 if($OldStyle) {
4011 $REMOVED_METHODS = "<h2>Removed Methods ($Removed_Number)</h2><hr/>\n".$REMOVED_METHODS;
4012 }
4013 else {
4014 $REMOVED_METHODS = "<h2>Removed Methods <span".getStyle("M", "Removed", $Removed_Number).">&nbsp;$Removed_Number&nbsp;</span></h2><hr/>\n".$REMOVED_METHODS;
4015 }
4016 $REMOVED_METHODS = $Anchor.$REMOVED_METHODS.$TOP_REF."<br/>\n";
4017 }
4018 return $REMOVED_METHODS;
4019}
4020
4021sub get_Report_MethodProblems($$)
4022{
4023 my ($TargetSeverity, $Level) = @_;
4024 my $METHOD_PROBLEMS = "";
4025 my (%ReportMap, %MethodChanges) = ();
4026
4027 foreach my $Method (sort keys(%CompatProblems))
4028 {
4029 my $ArchiveName = $MethodInfo{1}{$Method}{"Archive"};
4030 my $ClassName = get_ShortName($MethodInfo{1}{$Method}{"Class"}, 1);
4031
4032 foreach my $Kind (sort keys(%{$CompatProblems{$Method}}))
4033 {
4034 if($Kind eq "Added_Method"
4035 or $Kind eq "Removed_Method") {
4036 next;
4037 }
4038
4039 if(my $Severity = $MethodProblems_Kind{$Level}{$Kind})
4040 {
4041 if($Severity ne $TargetSeverity) {
4042 next;
4043 }
4044
4045 $MethodChanges{$Method}{$Kind} = $CompatProblems{$Method}{$Kind};
4046 $ReportMap{$ArchiveName}{$ClassName}{$Method} = 1;
4047 }
4048 }
4049 }
4050 my $ProblemsNum = 0;
4051 foreach my $ArchiveName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
4052 {
4053 foreach my $ClassName (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$ArchiveName}}))
4054 {
4055 my %NameSpace_Method = ();
4056 foreach my $Method (keys(%{$ReportMap{$ArchiveName}{$ClassName}})) {
4057 $NameSpace_Method{$MethodInfo{1}{$Method}{"Package"}}{$Method} = 1;
4058 }
4059
4060 my $ShowClass = $ClassName;
4061 $ShowClass=~s/<.*>//g;
4062
4063 foreach my $NameSpace (sort keys(%NameSpace_Method))
4064 {
4065 $METHOD_PROBLEMS .= "<span class='jar'>$ArchiveName</span>, <span class='cname'>".htmlSpecChars($ShowClass).".class</span><br/>\n";
4066 if($NameSpace) {
4067 $METHOD_PROBLEMS .= "<span class='pkg_t'>package</span> <span class='pkg'>$NameSpace</span><br/>\n";
4068 }
4069
4070 my @SortedMethods = sort {lc($MethodInfo{1}{$a}{"Signature"}) cmp lc($MethodInfo{1}{$b}{"Signature"})} keys(%{$NameSpace_Method{$NameSpace}});
4071 foreach my $Method (@SortedMethods)
4072 {
4073 my $ShortSignature = get_Signature($Method, 1, "Short");
4074 my $ClassName_Full = get_TypeName($MethodInfo{1}{$Method}{"Class"}, 1);
4075 my $METHOD_REPORT = "";
4076 my $ProblemNum = 1;
4077 foreach my $Kind (sort keys(%{$MethodChanges{$Method}}))
4078 {
4079 foreach my $Location (sort keys(%{$MethodChanges{$Method}{$Kind}}))
4080 {
4081 my %Problems = %{$MethodChanges{$Method}{$Kind}{$Location}};
4082
4083 my $Target = $Problems{"Target"};
4084
4085 my ($Change, $Effect) = ("", "");
4086 my $Old_Value = htmlSpecChars($Problems{"Old_Value"});
4087 my $New_Value = htmlSpecChars($Problems{"New_Value"});
4088
4089 if($Kind eq "Method_Became_Static")
4090 {
4091 $Change = "Method became <b>static</b>.\n";
4092 $Effect = "A client program may be interrupted by <b>NoSuchMethodError</b> exception.";
4093 }
4094 elsif($Kind eq "Method_Became_NonStatic")
4095 {
4096 $Change = "Method became <b>non-static</b>.\n";
4097 if($Level eq "Binary") {
4098 $Effect = "A client program may be interrupted by <b>NoSuchMethodError</b> exception.";
4099 }
4100 else {
4101 $Effect = "Recompilation of a client program may be terminated with the message: non-static method ".htmlSpecChars($ShortSignature)." cannot be referenced from a static context.";
4102 }
4103 }
4104 elsif($Kind eq "Changed_Method_Return_From_Void")
4105 {
4106 $Change = "Return value type has been changed from <b>void</b> to <b>".htmlSpecChars($New_Value)."</b>.\n";
4107 $Effect = "This method has been removed because the return type is part of the method signature.";
4108 }
4109 elsif($Kind eq "Static_Method_Became_Final")
4110 {# Source Only
4111 $Change = "Method became <b>final</b>.\n";
4112 $Effect = "Recompilation of a client program may be terminated with the message: ".htmlSpecChars($ShortSignature)." in client class C cannot override ".htmlSpecChars($ShortSignature)." in ".htmlSpecChars($ClassName_Full)."; overridden method is final.";
4113 }
4114 elsif($Kind eq "NonStatic_Method_Became_Final")
4115 {
4116 $Change = "Method became <b>final</b>.\n";
4117 if($Level eq "Binary") {
4118 $Effect = "A client program trying to reimplement this method may be interrupted by <b>VerifyError</b> exception.";
4119 }
4120 else {
4121 $Effect = "Recompilation of a client program may be terminated with the message: ".htmlSpecChars($ShortSignature)." in client class C cannot override ".htmlSpecChars($ShortSignature)." in ".htmlSpecChars($ClassName_Full)."; overridden method is final.";
4122 }
4123 }
4124 elsif($Kind eq "Method_Became_Abstract")
4125 {
4126 $Change = "Method became <b>abstract</b>.\n";
4127 if($Level eq "Binary") {
4128 $Effect = "A client program trying to create an instance of the method's class may be interrupted by <b>InstantiationError</b> exception.";
4129 }
4130 else {
4131 $Effect = "Recompilation of a client program may be terminated with the message: A client class C is not abstract and does not override abstract method ".htmlSpecChars($ShortSignature)." in ".htmlSpecChars($ClassName_Full).".";
4132 }
4133 }
4134 elsif($Kind eq "Method_Became_NonAbstract")
4135 {
4136 $Change = "Method became <b>non-abstract</b>.\n";
4137 if($Level eq "Binary") {
4138 $Effect = "A client program may change behavior.";
4139 }
4140 else {
4141 $Effect = "No effect.";
4142 }
4143 }
4144 elsif($Kind eq "Method_Became_Default")
4145 {
4146 $Change = "Method became <b>default</b>.\n";
4147 if($Level eq "Binary") {
4148 $Effect = "No effect.";
4149 }
4150 else {
4151 $Effect = "No effect.";
4152 }
4153 }
4154 elsif($Kind eq "Method_Became_NonDefault")
4155 {
4156 $Change = "Method became <b>non-default</b>.\n";
4157 if($Level eq "Binary") {
4158 $Effect = "A client program trying to create an instance of a class may be interrupted by <b>AbstractMethodError</b> exception.";
4159 }
4160 else {
4161 $Effect = "Recompilation of a client program may be terminated with the message: A client class C is not abstract and does not override abstract method ".htmlSpecChars($ShortSignature)." in ".htmlSpecChars($ClassName_Full).".";
4162 }
4163 }
4164 elsif($Kind eq "Method_Became_Synchronized")
4165 {
4166 $Change = "Method became <b>synchronized</b>.\n";
4167 $Effect = "A multi-threaded client program may change behavior.";
4168 }
4169 elsif($Kind eq "Method_Became_NonSynchronized")
4170 {
4171 $Change = "Method became <b>non-synchronized</b>.\n";
4172 $Effect = "A multi-threaded client program may change behavior.";
4173 }
4174 elsif($Kind eq "Changed_Method_Access")
4175 {
4176 $Change = "Access level has been changed from <span class='nowrap'><b>".htmlSpecChars($Old_Value)."</b></span> to <span class='nowrap'><b>".htmlSpecChars($New_Value)."</b></span>.";
4177 if($Level eq "Binary") {
4178 $Effect = "A client program may be interrupted by <b>IllegalAccessError</b> exception.";
4179 }
4180 else {
4181 $Effect = "Recompilation of a client program may be terminated with the message: ".htmlSpecChars($ShortSignature)." has $New_Value access in ".htmlSpecChars($ClassName_Full).".";
4182 }
4183 }
4184 elsif($Kind eq "Abstract_Method_Added_Checked_Exception")
4185 {# Source Only
4186 $Change = "Added <b>".htmlSpecChars($Target)."</b> exception thrown.\n";
4187 $Effect = "Recompilation of a client program may be terminated with the message: unreported exception ".htmlSpecChars($Target)." must be caught or declared to be thrown.";
4188 }
4189 elsif($Kind eq "NonAbstract_Method_Added_Checked_Exception")
4190 {
4191 $Change = "Added <b>".htmlSpecChars($Target)."</b> exception thrown.\n";
4192 if($Level eq "Binary") {
4193 $Effect = "A client program may be interrupted by added exception.";
4194 }
4195 else {
4196 $Effect = "Recompilation of a client program may be terminated with the message: unreported exception ".htmlSpecChars($Target)." must be caught or declared to be thrown.";
4197 }
4198 }
4199 elsif($Kind eq "Abstract_Method_Removed_Checked_Exception")
4200 {# Source Only
4201 $Change = "Removed <b>".htmlSpecChars($Target)."</b> exception thrown.\n";
4202 $Effect = "Recompilation of a client program may be terminated with the message: cannot override ".htmlSpecChars($ShortSignature)." in ".htmlSpecChars($ClassName_Full)."; overridden method does not throw ".htmlSpecChars($Target).".";
4203 }
4204 elsif($Kind eq "NonAbstract_Method_Removed_Checked_Exception")
4205 {
4206 $Change = "Removed <b>".htmlSpecChars($Target)."</b> exception thrown.\n";
4207 if($Level eq "Binary") {
4208 $Effect = "A client program may change behavior because the removed exception will not be thrown any more and client will not catch and handle it.";
4209 }
4210 else {
4211 $Effect = "Recompilation of a client program may be terminated with the message: cannot override ".htmlSpecChars($ShortSignature)." in ".htmlSpecChars($ClassName_Full)."; overridden method does not throw ".htmlSpecChars($Target).".";
4212 }
4213 }
4214 elsif($Kind eq "Added_Unchecked_Exception")
4215 {# Binary Only
4216 $Change = "Added <b>".htmlSpecChars($Target)."</b> exception thrown.\n";
4217 $Effect = "A client program may be interrupted by added exception.";
4218 }
4219 elsif($Kind eq "Removed_Unchecked_Exception")
4220 {# Binary Only
4221 $Change = "Removed <b>".htmlSpecChars($Target)."</b> exception thrown.\n";
4222 $Effect = "A client program may change behavior because the removed exception will not be thrown any more and client will not catch and handle it.";
4223 }
4224 if($Change)
4225 {
4226 $METHOD_REPORT .= "<tr><th>$ProblemNum</th><td>".$Change."</td><td>".$Effect."</td></tr>\n";
4227 $ProblemNum += 1;
4228 $ProblemsNum += 1;
4229 }
4230 }
4231 }
4232 $ProblemNum -= 1;
4233 if($METHOD_REPORT)
4234 {
4235 my $ShowMethod = highLight_Signature_Italic_Color($Method, 1);
4236 if($NameSpace)
4237 {
4238 $METHOD_REPORT = cut_Namespace($METHOD_REPORT, $NameSpace);
4239 $ShowMethod = cut_Namespace($ShowMethod, $NameSpace);
4240 }
4241
4242 $METHOD_PROBLEMS .= $ContentSpanStart."<span class='ext'>[+]</span> ".$ShowMethod;
4243 if($OldStyle) {
4244 $METHOD_PROBLEMS .= " ($ProblemNum)";
4245 }
4246 else {
4247 $METHOD_PROBLEMS .= " <span".getStyle("M", $TargetSeverity, $ProblemNum).">&nbsp;$ProblemNum&nbsp;</span>";
4248 }
4249 $METHOD_PROBLEMS .= $ContentSpanEnd."<br/>\n";
4250 $METHOD_PROBLEMS .= $ContentDivStart;
4251
4252 if(not $Compact) {
4253 $METHOD_PROBLEMS .= "<span class='mngl'>&#160;&#160;&#160;[mangled: <b>".htmlSpecChars($Method)."</b>]</span><br/>\n";
4254 }
4255
4256 $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";
4257
4258 }
4259 }
4260
4261 $METHOD_PROBLEMS .= "<br/>";
4262 }
4263 }
4264 }
4265 if($METHOD_PROBLEMS)
4266 {
4267 $METHOD_PROBLEMS = insertIDs($METHOD_PROBLEMS);
4268
4269 my $Title = "Problems with Methods, $TargetSeverity Severity";
4270 if($TargetSeverity eq "Safe")
4271 { # Safe Changes
4272 $Title = "Other Changes in Methods";
4273 }
4274 if($OldStyle) {
4275 $METHOD_PROBLEMS = "<h2>$Title ($ProblemsNum)</h2><hr/>\n".$METHOD_PROBLEMS;
4276 }
4277 else {
4278 $METHOD_PROBLEMS = "<h2>$Title <span".getStyle("M", $TargetSeverity, $ProblemsNum).">&nbsp;$ProblemsNum&nbsp;</span></h2><hr/>\n".$METHOD_PROBLEMS;
4279 }
4280 $METHOD_PROBLEMS = "<a name='".get_Anchor("Method", $Level, $TargetSeverity)."'></a>\n".$METHOD_PROBLEMS;
4281 $METHOD_PROBLEMS .= $TOP_REF."<br/>\n";
4282 }
4283 return $METHOD_PROBLEMS;
4284}
4285
4286sub get_Report_TypeProblems($$)
4287{
4288 my ($TargetSeverity, $Level) = @_;
4289 my $TYPE_PROBLEMS = "";
4290 my %ReportMap = ();
4291 my %TypeChanges_Sev = ();
4292
4293 foreach my $TypeName (keys(%{$TypeChanges{$Level}}))
4294 {
4295 my $ArchiveName = $TypeInfo{1}{$TName_Tid{1}{$TypeName}}{"Archive"};
4296
4297 foreach my $Kind (keys(%{$TypeChanges{$Level}{$TypeName}}))
4298 {
4299 my $Severity = $TypeProblems_Kind{$Level}{$Kind};
4300
4301 if($Severity ne $TargetSeverity) {
4302 next;
4303 }
4304
4305 foreach my $Location (keys(%{$TypeChanges{$Level}{$TypeName}{$Kind}}))
4306 {
4307 $ReportMap{$ArchiveName}{$TypeName} = 1;
4308 $TypeChanges_Sev{$TypeName}{$Kind}{$Location} = $TypeChanges{$Level}{$TypeName}{$Kind}{$Location};
4309 }
4310 }
4311 }
4312
4313 my $ProblemsNum = 0;
4314 foreach my $ArchiveName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
4315 {
4316 my ($HEADER_REPORT, %NameSpace_Type) = ();
4317 foreach my $TypeName (keys(%{$ReportMap{$ArchiveName}}))
4318 {
4319 $NameSpace_Type{$TypeInfo{1}{$TName_Tid{1}{$TypeName}}{"Package"}}{$TypeName} = 1;
4320 }
4321 foreach my $NameSpace (sort keys(%NameSpace_Type))
4322 {
4323 $TYPE_PROBLEMS .= "<span class='jar'>$ArchiveName</span><br/>\n";
4324 if($NameSpace) {
4325 $TYPE_PROBLEMS .= "<span class='pkg_t'>package</span> <span class='pkg'>".$NameSpace."</span><br/>\n";
4326 }
4327
4328 my @SortedTypes = sort {lc($a) cmp lc($b)} keys(%{$NameSpace_Type{$NameSpace}});
4329 foreach my $TypeName (@SortedTypes)
4330 {
4331 my $TypeId = $TName_Tid{1}{$TypeName};
4332 my $ProblemNum = 1;
4333 my $TYPE_REPORT = "";
4334 my (%Kinds_Locations, %Kinds_Target) = ();
4335 foreach my $Kind (sort keys(%{$TypeChanges_Sev{$TypeName}}))
4336 {
4337 foreach my $Location (sort keys(%{$TypeChanges_Sev{$TypeName}{$Kind}}))
4338 {
4339 $Kinds_Locations{$Kind}{$Location} = 1;
4340
4341 my $Target = $TypeChanges_Sev{$TypeName}{$Kind}{$Location}{"Target"};
4342 if($Kinds_Target{$Kind}{$Target}) {
4343 next;
4344 }
4345 $Kinds_Target{$Kind}{$Target} = 1;
4346
4347 my ($Change, $Effect) = ("", "");
4348 my %Problems = %{$TypeChanges_Sev{$TypeName}{$Kind}{$Location}};
4349
4350 my $Old_Value = $Problems{"Old_Value"};
4351 my $New_Value = $Problems{"New_Value"};
4352 my $Field_Type = $Problems{"Field_Type"};
4353 my $Field_Value = $Problems{"Field_Value"};
4354 my $Type_Type = $Problems{"Type_Type"};
4355
4356 if($Kind eq "NonAbstract_Class_Added_Abstract_Method")
4357 {
4358 $Change = "Abstract method ".black_Name($Target, 2)." has been added to this $Type_Type.";
4359 if($Level eq "Binary") {
4360 $Effect = "This class became <b>abstract</b> and a client program may be interrupted by <b>InstantiationError</b> exception.";
4361 }
4362 else {
4363 $Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method <b>".htmlSpecChars(get_Signature($Target, 2, "Short"))."</b> in <b>".htmlSpecChars(get_TypeName($MethodInfo{2}{$Target}{"Class"}, 2))."</b>.";
4364 }
4365 }
4366 elsif($Kind eq "Abstract_Class_Added_Abstract_Method")
4367 {
4368 $Change = "Abstract method ".black_Name($Target, 2)." has been added to this $Type_Type.";
4369 if($Level eq "Binary") {
4370 $Effect = "No effect.";
4371 }
4372 else {
4373 $Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method <b>".htmlSpecChars(get_Signature($Target, 2, "Short"))."</b> in <b>".htmlSpecChars(get_TypeName($MethodInfo{2}{$Target}{"Class"}, 2))."</b>.";
4374 }
4375 }
4376 elsif($Kind eq "Abstract_Class_Added_Abstract_Method_Invoked_By_Others")
4377 {
4378 $Change = "Abstract method ".black_Name($Target, 2)." has been added to this $Type_Type.";
4379 if($Level eq "Binary") {
4380 $Effect = "A client program may be interrupted by <b>AbstractMethodError</b> exception. Added abstract method is called in 2nd library version by the method ".black_Name($Problems{"InvokedBy"}, 1)." and may not be implemented by old clients.";
4381 }
4382 else {
4383 $Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method <b>".htmlSpecChars(get_Signature($Target, 2, "Short"))."</b> in <b>".htmlSpecChars(get_TypeName($MethodInfo{2}{$Target}{"Class"}, 2))."</b>.";
4384 }
4385 }
4386 elsif($Kind eq "Class_Removed_Abstract_Method"
4387 or $Kind eq "Interface_Removed_Abstract_Method")
4388 {
4389 $Change = "Abstract method ".black_Name($Target, 1)." has been removed from this $Type_Type.";
4390 if($Level eq "Binary") {
4391 $Effect = "A client program may be interrupted by <b>NoSuchMethodError</b> exception.";
4392 }
4393 else {
4394 $Effect = "Recompilation of a client program may be terminated with the message: cannot find method <b>".htmlSpecChars(get_Signature($Target, 1, "Short"))."</b> in $Type_Type <b>".htmlSpecChars(get_TypeName($MethodInfo{1}{$Target}{"Class"}, 1))."</b>.";
4395 }
4396 }
4397 elsif($Kind eq "Interface_Added_Abstract_Method")
4398 {
4399 $Change = "Abstract method ".black_Name($Target, 2)." has been added to this $Type_Type.";
4400 if($Level eq "Binary") {
4401 $Effect = "No effect.";
4402 }
4403 else {
4404 $Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method <b>".htmlSpecChars(get_Signature($Target, 2, "Short"))."</b> in <b>".htmlSpecChars(get_TypeName($MethodInfo{2}{$Target}{"Class"}, 2))."</b>.";
4405 }
4406 }
4407 elsif($Kind eq "Interface_Added_Abstract_Method_Invoked_By_Others")
4408 {
4409 $Change = "Abstract method ".black_Name($Target, 2)." has been added to this $Type_Type.";
4410 if($Level eq "Binary") {
4411 $Effect = "A client program may be interrupted by <b>AbstractMethodError</b> exception. Added abstract method is called in 2nd library version by the method ".black_Name($Problems{"InvokedBy"}, 1)." and may not be implemented by old clients.";
4412 }
4413 else {
4414 $Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method <b>".htmlSpecChars(get_Signature($Target, 2, "Short"))."</b> in <b>".htmlSpecChars(get_TypeName($MethodInfo{2}{$Target}{"Class"}, 2))."</b>.";
4415 }
4416 }
4417 elsif($Kind eq "Class_Method_Became_Abstract")
4418 {
4419 $Change = "Method ".black_Name($Target, 1)." became <b>abstract</b>.";
4420 if($Level eq "Binary") {
4421 $Effect = "A client program may be interrupted by <b>InstantiationError</b> exception.";
4422 }
4423 else {
4424 $Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method <b>".htmlSpecChars(get_Signature($Target, 1, "Short"))."</b> in <b>".htmlSpecChars(get_TypeName($MethodInfo{1}{$Target}{"Class"}, 1))."</b>.";
4425 }
4426 }
4427 elsif($Kind eq "Class_Method_Became_NonAbstract")
4428 {
4429 $Change = "Abstract method ".black_Name($Target, 1)." became <b>non-abstract</b>.";
4430 if($Level eq "Binary") {
4431 $Effect = "Some methods in this class may change behavior.";
4432 }
4433 else {
4434 $Effect = "No effect.";
4435 }
4436 }
4437 elsif($Kind eq "Interface_Method_Became_NonDefault")
4438 {
4439 $Change = "Method ".black_Name($Target, 1)." became <b>non-default</b>.";
4440 if($Level eq "Binary") {
4441 $Effect = "A client program may be interrupted by <b>AbstractMethodError</b> exception.";
4442 }
4443 else {
4444 $Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method <b>".htmlSpecChars(get_Signature($Target, 1, "Short"))."</b> in <b>".htmlSpecChars(get_TypeName($MethodInfo{1}{$Target}{"Class"}, 1))."</b>.";
4445 }
4446 }
4447 elsif($Kind eq "Interface_Method_Became_Default")
4448 {
4449 $Change = "Method ".black_Name($Target, 1)." became <b>default</b>.";
4450 if($Level eq "Binary") {
4451 $Effect = "No effect.";
4452 }
4453 else {
4454 $Effect = "No effect.";
4455 }
4456 }
4457 elsif($Kind eq "Class_Overridden_Method")
4458 {
4459 $Change = "Method ".black_Name($Old_Value, 2)." has been overridden by ".black_Name($New_Value, 2);
4460 $Effect = "Method ".black_Name($New_Value, 2)." will be called instead of ".black_Name($Old_Value, 2)." in a client program.";
4461 }
4462 elsif($Kind eq "Class_Method_Moved_Up_Hierarchy")
4463 {
4464 $Change = "Method ".black_Name($Old_Value, 1)." has been moved up type hierarchy to ".black_Name($New_Value, 2);
4465 $Effect = "Method ".black_Name($New_Value, 2)." will be called instead of ".black_Name($Old_Value, 1)." in a client program.";
4466 }
4467 elsif($Kind eq "Abstract_Class_Added_Super_Interface")
4468 {
4469 $Change = "Added super-interface <b>".htmlSpecChars($Target)."</b>.";
4470 if($Level eq "Binary")
4471 {
4472 $Effect = "No effect.";
4473 }
4474 else {
4475 $Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method in <b>".htmlSpecChars($Target)."</b>.";
4476 }
4477 }
4478 elsif($Kind eq "Abstract_Class_Added_Super_Interface_Invoked_By_Others")
4479 {
4480 $Change = "Added super-interface <b>".htmlSpecChars($Target)."</b>.";
4481 if($Level eq "Binary") {
4482 $Effect = "If abstract methods from an added super-interface must be implemented by client then it may be interrupted by <b>AbstractMethodError</b> exception.<br/><br/>Abstract method ".black_Name_S(htmlSpecChars($Problems{"Invoked"}))." from the added super-interface is called by the method ".black_Name($Problems{"InvokedBy"}, 2)." in 2nd library version and may not be implemented by old clients.";
4483 }
4484 else {
4485 $Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method in <b>".htmlSpecChars($Target)."</b>.";
4486 }
4487 }
4488 elsif($Kind eq "Abstract_Class_Added_Super_Interface_With_Implemented_Methods")
4489 {
4490 $Change = "Added super-interface <b>".htmlSpecChars($Target)."</b>.";
4491 $Effect = "No effect.";
4492 }
4493 elsif($Kind eq "Interface_Added_Super_Interface")
4494 {
4495 $Change = "Added super-interface <b>".htmlSpecChars($Target)."</b>.";
4496 if($Level eq "Binary") {
4497 $Effect = "No effect.";
4498 }
4499 else {
4500 $Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method in <b>".htmlSpecChars($Target)."</b>.";
4501 }
4502 }
4503 elsif($Kind eq "Interface_Added_Super_Interface_Used_By_Others")
4504 {
4505 $Change = "Added super-interface <b>".htmlSpecChars($Target)."</b>.";
4506 if($Level eq "Binary")
4507 {
4508 $Effect = "If abstract methods from an added super-interface must be implemented by client then it may be interrupted by <b>AbstractMethodError</b> exception.<br/><br/>Abstract method ".black_Name_S(htmlSpecChars($Problems{"Invoked"}))." from the added super-interface is called by the method ".black_Name($Problems{"InvokedBy"}, 2)." in 2nd library version and may not be implemented by old clients.";
4509 }
4510 else {
4511 $Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method in <b>".htmlSpecChars($Target)."</b>.";
4512 }
4513 }
4514 elsif($Kind eq "Interface_Added_Super_Interface_With_Implemented_Methods")
4515 {
4516 $Change = "Added super-interface <b>".htmlSpecChars($Target)."</b>.";
4517 if($Level eq "Binary") {
4518 $Effect = "No effect.";
4519 }
4520 else {
4521 $Effect = "No effect.";
4522 }
4523 }
4524 elsif($Kind eq "Interface_Added_Super_Constant_Interface")
4525 {
4526 $Change = "Added super-interface <b>".htmlSpecChars($Target)."</b> containing constants only.";
4527 if($Level eq "Binary") {
4528 $Effect = "A static field from a super-interface of a client class may hide a field (with the same name) inherited from a super-class and cause <b>IncompatibleClassChangeError</b> exception.";
4529 }
4530 else {
4531 $Effect = "A static field from a super-interface of a client class may hide a field (with the same name) inherited from a super-class. Recompilation of a client class may be terminated with the message: reference to variable is ambiguous.";
4532 }
4533 }
4534 elsif($Kind eq "Interface_Removed_Super_Interface"
4535 or $Kind eq "Class_Removed_Super_Interface")
4536 {
4537 $Change = "Removed super-interface <b>".htmlSpecChars($Target)."</b>.";
4538 if($Level eq "Binary") {
4539 $Effect = "A client program may be interrupted by <b>NoSuchMethodError</b> exception.";
4540 }
4541 else {
4542 $Effect = "Recompilation of a client program may be terminated with the message: cannot find method in $Type_Type <b>".htmlSpecChars($TypeName)."</b>.";
4543 }
4544 }
4545 elsif($Kind eq "Interface_Removed_Super_Constant_Interface")
4546 {
4547 $Change = "Removed super-interface <b>".htmlSpecChars($Target)."</b> containing constants only.";
4548 if($Level eq "Binary") {
4549 $Effect = "No effect.";
4550 }
4551 else {
4552 $Effect = "Recompilation of a client program may be terminated with the message: cannot find variable in $Type_Type <b>".htmlSpecChars($TypeName)."</b>.";
4553 }
4554 }
4555 elsif($Kind eq "Added_Super_Class")
4556 {
4557 $Change = "Added super-class <b>".htmlSpecChars($Target)."</b>.";
4558 if($Level eq "Binary") {
4559 $Effect = "A static field from a super-interface of a client class may hide a field (with the same name) inherited from new super-class and cause <b>IncompatibleClassChangeError</b> exception.";
4560 }
4561 else {
4562 $Effect = "A static field from a super-interface of a client class may hide a field (with the same name) inherited from new super-class. Recompilation of a client class may be terminated with the message: reference to variable is ambiguous.";
4563 }
4564 }
4565 elsif($Kind eq "Abstract_Class_Added_Super_Abstract_Class")
4566 {
4567 $Change = "Added abstract super-class <b>".htmlSpecChars($Target)."</b>.";
4568 if($Level eq "Binary") {
4569 $Effect = "No effect.";
4570 }
4571 else {
4572 $Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method in <b>".htmlSpecChars($Target)."</b>.";
4573 }
4574 }
4575 elsif($Kind eq "Abstract_Class_Added_Super_Abstract_Class_Invoked_By_Others")
4576 {
4577 $Change = "Added abstract super-class <b>".htmlSpecChars($Target)."</b>.";
4578 if($Level eq "Binary") {
4579 $Effect = "If abstract methods from an added super-class must be implemented by client then it may be interrupted by <b>AbstractMethodError</b> exception.<br/><br/>Abstract method ".black_Name_S(htmlSpecChars($Problems{"Invoked"}))." from the added abstract super-class is called by the method ".black_Name($Problems{"InvokedBy"}, 2)." in 2nd library version and may not be implemented by old clients.";
4580 }
4581 else {
4582 $Effect = "Recompilation of a client program may be terminated with the message: a client class C is not abstract and does not override abstract method in <b>".htmlSpecChars($Target)."</b>.";
4583 }
4584 }
4585 elsif($Kind eq "Removed_Super_Class")
4586 {
4587 $Change = "Removed super-class <b>".htmlSpecChars($Target)."</b>.";
4588 if($Level eq "Binary") {
4589 $Effect = "Access of a client program to the fields or methods of the old super-class may be interrupted by <b>NoSuchFieldError</b> or <b>NoSuchMethodError</b> exceptions.";
4590 }
4591 else {
4592 $Effect = "Recompilation of a client program may be terminated with the message: cannot find variable (or method) in <b>".htmlSpecChars($TypeName)."</b>.";
4593 }
4594 }
4595 elsif($Kind eq "Changed_Super_Class")
4596 {
4597 $Change = "Superclass has been changed from <b>".htmlSpecChars($Old_Value)."</b> to <b>".htmlSpecChars($New_Value)."</b>.";
4598 if($Level eq "Binary") {
4599 $Effect = "1) Access of a client program to the fields or methods of the old super-class may be interrupted by <b>NoSuchFieldError</b> or <b>NoSuchMethodError</b> exceptions.<br/>2) A static field from a super-interface of a client class may hide a field (with the same name) inherited from new super-class and cause <b>IncompatibleClassChangeError</b> exception.";
4600 }
4601 else {
4602 $Effect = "1) Recompilation of a client program may be terminated with the message: cannot find variable (or method) in <b>".htmlSpecChars($TypeName)."</b>.<br/>2) A static field from a super-interface of a client class may hide a field (with the same name) inherited from new super-class. Recompilation of a client class may be terminated with the message: reference to variable is ambiguous.";
4603 }
4604 }
4605 elsif($Kind eq "Class_Added_Field")
4606 {
4607 $Change = "Field <b>$Target</b> has been added to this class.";
4608 if($Level eq "Binary")
4609 {
4610 $Effect = "No effect.";
4611 # $Effect .= "<br/><b>NOTE</b>: A static field from a super-interface of a client class may hide an added field (with the same name) inherited from the super-class of a client class and cause <b>IncompatibleClassChangeError</b> exception.";
4612 }
4613 else
4614 {
4615 $Effect = "No effect.";
4616 # $Effect .= "<br/><b>NOTE</b>: A static field from a super-interface of a client class may hide an added field (with the same name) inherited from the super-class of a client class. Recompilation of a client class may be terminated with the message: reference to <b>$Target</b> is ambiguous.";
4617 }
4618 }
4619 elsif($Kind eq "Interface_Added_Field")
4620 {
4621 $Change = "Field <b>$Target</b> has been added to this interface.";
4622 if($Level eq "Binary") {
4623 $Effect = "No effect.<br/><b>NOTE</b>: An added static field from a super-interface of a client class may hide a field (with the same name) inherited from the super-class of a client class and cause <b>IncompatibleClassChangeError</b> exception.";
4624 }
4625 else {
4626 $Effect = "No effect.<br/><b>NOTE</b>: An added static field from a super-interface of a client class may hide a field (with the same name) inherited from the super-class of a client class. Recompilation of a client class may be terminated with the message: reference to <b>$Target</b> is ambiguous.";
4627 }
4628 }
4629 elsif($Kind eq "Renamed_Field")
4630 {
4631 $Change = "Field <b>$Target</b> has been renamed to <b>".htmlSpecChars($New_Value)."</b>.";
4632 if($Level eq "Binary") {
4633 $Effect = "A client program may be interrupted by <b>NoSuchFieldError</b> exception.";
4634 }
4635 else {
4636 $Effect = "Recompilation of a client program may be terminated with the message: cannot find variable <b>$Target</b> in <b>".htmlSpecChars($TypeName)."</b>.";
4637 }
4638 }
4639 elsif($Kind eq "Renamed_Constant_Field")
4640 {
4641 if($Level eq "Binary") {
4642 $Change = "Field <b>$Target</b> (".htmlSpecChars($Field_Type).") with the compile-time constant value <b>$Field_Value</b> has been renamed to <b>".htmlSpecChars($New_Value)."</b>.";
4643 $Effect = "A client program may change behavior.";
4644 }
4645 else {
4646 $Change = "Field <b>$Target</b> has been renamed to <b>".htmlSpecChars($New_Value)."</b>.";
4647 $Effect = "Recompilation of a client program may be terminated with the message: cannot find variable <b>$Target</b> in <b>".htmlSpecChars($TypeName)."</b>.";
4648 }
4649 }
4650 elsif($Kind eq "Removed_NonConstant_Field")
4651 {
4652 $Change = "Field <b>$Target</b> of type ".htmlSpecChars($Field_Type)." has been removed from this $Type_Type.";
4653 if($Level eq "Binary") {
4654 $Effect = "A client program may be interrupted by <b>NoSuchFieldError</b> exception.";
4655 }
4656 else {
4657 $Effect = "Recompilation of a client program may be terminated with the message: cannot find variable <b>$Target</b> in <b>".htmlSpecChars($TypeName)."</b>.";
4658 }
4659 }
4660 elsif($Kind eq "Removed_Constant_Field")
4661 {
4662 $Change = "Field <b>$Target</b> (".htmlSpecChars($Field_Type).") with the compile-time constant value <b>$Field_Value</b> has been removed from this $Type_Type.";
4663 if($Level eq "Binary") {
4664 $Effect = "A client program may change behavior.";
4665 }
4666 else {
4667 $Effect = "Recompilation of a client program may be terminated with the message: cannot find variable <b>$Target</b> in <b>".htmlSpecChars($TypeName)."</b>.";
4668 }
4669 }
4670 elsif($Kind eq "Changed_Field_Type")
4671 {
4672 $Change = "Type of field <b>$Target</b> has been changed from <span class='nowrap'><b>".htmlSpecChars($Old_Value)."</b></span> to <span class='nowrap'><b>".htmlSpecChars($New_Value)."</b></span>.";
4673 if($Level eq "Binary") {
4674 $Effect = "A client program may be interrupted by <b>NoSuchFieldError</b> exception.";
4675 }
4676 else {
4677 $Effect = "Recompilation of a client program may be terminated with the message: incompatible types, found: <b>".htmlSpecChars($Old_Value)."</b>, required: <b>".htmlSpecChars($New_Value)."</b>.";
4678 }
4679 }
4680 elsif($Kind eq "Changed_Field_Access")
4681 {
4682 $Change = "Access level of field <b>$Target</b> has been changed from <span class='nowrap'><b>$Old_Value</b></span> to <span class='nowrap'><b>$New_Value</b></span>.";
4683 if($Level eq "Binary") {
4684 $Effect = "A client program may be interrupted by <b>IllegalAccessError</b> exception.";
4685 }
4686 else
4687 {
4688 if($New_Value eq "package-private") {
4689 $Effect = "Recompilation of a client program may be terminated with the message: <b>$Target</b> is not public in <b>".htmlSpecChars($TypeName)."</b>; cannot be accessed from outside package.";
4690 }
4691 else {
4692 $Effect = "Recompilation of a client program may be terminated with the message: <b>$Target</b> has <b>$New_Value</b> access in <b>".htmlSpecChars($TypeName)."</b>.";
4693 }
4694 }
4695 }
4696 elsif($Kind eq "Changed_Final_Field_Value")
4697 { # Binary Only
4698 $Change = "Value of final field <b>$Target</b> (<b>$Field_Type</b>) has been changed from <span class='nowrap'><b>".htmlSpecChars($Old_Value)."</b></span> to <span class='nowrap'><b>".htmlSpecChars($New_Value)."</b></span>.";
4699 $Effect = "Old value of the field will be inlined to the client code at compile-time and will be used instead of a new one.";
4700 }
4701 elsif($Kind eq "Changed_Final_Version_Field_Value")
4702 { # Binary Only
4703 $Change = "Value of final field <b>$Target</b> (<b>$Field_Type</b>) has been changed from <span class='nowrap'><b>".htmlSpecChars($Old_Value)."</b></span> to <span class='nowrap'><b>".htmlSpecChars($New_Value)."</b></span>.";
4704 $Effect = "Old value of the field will be inlined to the client code at compile-time and will be used instead of a new one.";
4705 }
4706 elsif($Kind eq "Field_Became_Final")
4707 {
4708 $Change = "Field <b>$Target</b> became <b>final</b>.";
4709 if($Level eq "Binary") {
4710 $Effect = "A client program may be interrupted by <b>IllegalAccessError</b> exception when attempt to assign new values to the field.";
4711 }
4712 else {
4713 $Effect = "Recompilation of a client program may be terminated with the message: cannot assign a value to final variable $Target.";
4714 }
4715 }
4716 elsif($Kind eq "Field_Became_NonFinal")
4717 { # Binary Only
4718 $Change = "Field <b>$Target</b> became <b>non-final</b>.";
4719 $Effect = "Old value of the field will be inlined to the client code at compile-time and will be used instead of a new one.";
4720 }
4721 elsif($Kind eq "NonConstant_Field_Became_Static")
4722 { # Binary Only
4723 $Change = "Non-final field <b>$Target</b> became <b>static</b>.";
4724 $Effect = "A client program may be interrupted by <b>IncompatibleClassChangeError</b> exception.";
4725 }
4726 elsif($Kind eq "NonConstant_Field_Became_NonStatic")
4727 {
4728 if($Level eq "Binary") {
4729 $Change = "Non-constant field <b>$Target</b> became <b>non-static</b>.";
4730 $Effect = "A client program may be interrupted by <b>IncompatibleClassChangeError</b> exception.";
4731 }
4732 else {
4733 $Change = "Field <b>$Target</b> became <b>non-static</b>.";
4734 $Effect = "Recompilation of a client program may be terminated with the message: non-static variable <b>$Target</b> cannot be referenced from a static context.";
4735 }
4736 }
4737 elsif($Kind eq "Constant_Field_Became_NonStatic")
4738 { # Source Only
4739 $Change = "Field <b>$Target</b> became <b>non-static</b>.";
4740 $Effect = "Recompilation of a client program may be terminated with the message: non-static variable <b>$Target</b> cannot be referenced from a static context.";
4741 }
4742 elsif($Kind eq "Class_Became_Interface")
4743 {
4744 $Change = "This <b>class</b> became <b>interface</b>.";
4745 if($Level eq "Binary") {
4746 $Effect = "A client program may be interrupted by <b>IncompatibleClassChangeError</b> or <b>InstantiationError</b> exception dependent on the usage of this class.";
4747 }
4748 else {
4749 $Effect = "Recompilation of a client program may be terminated with the message: <b>".htmlSpecChars($TypeName)."</b> is abstract; cannot be instantiated.";
4750 }
4751 }
4752 elsif($Kind eq "Interface_Became_Class")
4753 {
4754 $Change = "This <b>interface</b> became <b>class</b>.";
4755 if($Level eq "Binary") {
4756 $Effect = "A client program may be interrupted by <b>IncompatibleClassChangeError</b> exception.";
4757 }
4758 else {
4759 $Effect = "Recompilation of a client program may be terminated with the message: interface expected.";
4760 }
4761 }
4762 elsif($Kind eq "Class_Became_Final")
4763 {
4764 $Change = "This class became <b>final</b>.";
4765 if($Level eq "Binary") {
4766 $Effect = "A client program may be interrupted by <b>VerifyError</b> exception.";
4767 }
4768 else {
4769 $Effect = "Recompilation of a client program may be terminated with the message: cannot inherit from final <b>".htmlSpecChars($TypeName)."</b>.";
4770 }
4771 }
4772 elsif($Kind eq "Class_Became_Abstract")
4773 {
4774 $Change = "This class became <b>abstract</b>.";
4775 if($Level eq "Binary") {
4776 $Effect = "A client program may be interrupted by <b>InstantiationError</b> exception.";
4777 }
4778 else {
4779 $Effect = "Recompilation of a client program may be terminated with the message: <b>".htmlSpecChars($TypeName)."</b> is abstract; cannot be instantiated.";
4780 }
4781 }
4782 elsif($Kind eq "Removed_Class")
4783 {
4784 $Change = "This class has been removed.";
4785 if($Level eq "Binary") {
4786 $Effect = "A client program may be interrupted by <b>NoClassDefFoundError</b> exception.";
4787 }
4788 else {
4789 $Effect = "Recompilation of a client program may be terminated with the message: cannot find class <b>".htmlSpecChars($TypeName)."</b>.";
4790 }
4791 }
4792 elsif($Kind eq "Removed_Interface")
4793 {
4794 $Change = "This interface has been removed.";
4795 if($Level eq "Binary") {
4796 $Effect = "A client program may be interrupted by <b>NoClassDefFoundError</b> exception.";
4797 }
4798 else {
4799 $Effect = "Recompilation of a client program may be terminated with the message: cannot find class <b>".htmlSpecChars($TypeName)."</b>.";
4800 }
4801 }
4802 elsif($Kind eq "Removed_Annotation")
4803 {
4804 $Change = "This annotation type has been removed.";
4805 if($Level eq "Binary") {
4806 $Effect = "No effect.";
4807 }
4808 else {
4809 $Effect = "Recompilation of a client program may be terminated with the error message: cannot find symbol <b>\@".htmlSpecChars($TypeName)."</b>.";
4810 }
4811 }
4812 if($Change)
4813 {
4814 $TYPE_REPORT .= "<tr><th>$ProblemNum</th><td>".$Change."</td><td>".$Effect."</td></tr>\n";
4815 $ProblemNum += 1;
4816 $ProblemsNum += 1;
4817 $Kinds_Locations{$Kind}{$Location} = 1;
4818 }
4819 }
4820 }
4821 $ProblemNum -= 1;
4822 if($TYPE_REPORT)
4823 {
4824 my $Affected = "";
4825 if(not defined $TypeInfo{1}{$TypeId}{"Annotation"}) {
4826 $Affected = getAffectedMethods($Level, $TypeName, \%Kinds_Locations);
4827 }
4828
4829 my $ShowType = $TypeName;
4830 if($NameSpace)
4831 {
4832 $TYPE_REPORT = cut_Namespace($TYPE_REPORT, $NameSpace);
4833 $ShowType = cut_Namespace($ShowType, $NameSpace);
4834 $Affected = cut_Namespace($Affected, $NameSpace);
4835 }
4836
4837 $TYPE_PROBLEMS .= $ContentSpanStart."<span class='ext'>[+]</span> ".htmlSpecChars($ShowType);
4838 if($OldStyle) {
4839 $TYPE_PROBLEMS .= " ($ProblemNum)";
4840 }
4841 else {
4842 $TYPE_PROBLEMS .= " <span".getStyle("T", $TargetSeverity, $ProblemNum).">&nbsp;$ProblemNum&nbsp;</span>";
4843 }
4844 $TYPE_PROBLEMS .= $ContentSpanEnd."<br/>\n";
4845 $TYPE_PROBLEMS .= $ContentDivStart."<table class='ptable'><tr>";
4846 $TYPE_PROBLEMS .= "<th width='2%'></th><th width='47%'>Change</th><th>Effect</th>";
4847 $TYPE_PROBLEMS .= "</tr>$TYPE_REPORT</table>".$Affected."<br/><br/>$ContentDivEnd\n";
4848 }
4849 }
4850
4851 $TYPE_PROBLEMS .= "<br/>";
4852 }
4853 }
4854 if($TYPE_PROBLEMS)
4855 {
4856 $TYPE_PROBLEMS = insertIDs($TYPE_PROBLEMS);
4857
4858 my $Title = "Problems with Data Types, $TargetSeverity Severity";
4859 if($TargetSeverity eq "Safe")
4860 { # Safe Changes
4861 $Title = "Other Changes in Data Types";
4862 }
4863 if($OldStyle) {
4864 $TYPE_PROBLEMS = "<h2>$Title ($ProblemsNum)</h2><hr/>\n".$TYPE_PROBLEMS;
4865 }
4866 else {
4867 $TYPE_PROBLEMS = "<h2>$Title <span".getStyle("T", $TargetSeverity, $ProblemsNum).">&nbsp;$ProblemsNum&nbsp;</span></h2><hr/>\n".$TYPE_PROBLEMS;
4868 }
4869 $TYPE_PROBLEMS = "<a name='".get_Anchor("Type", $Level, $TargetSeverity)."'></a>\n".$TYPE_PROBLEMS;
4870 $TYPE_PROBLEMS .= $TOP_REF."<br/>\n";
4871 }
4872 return $TYPE_PROBLEMS;
4873}
4874
4875sub cut_Namespace($$)
4876{
4877 my ($N, $Ns) = @_;
4878 $N=~s/(\W|\A)\Q$Ns\E\.(\w)/$1$2/g;
4879 return $N;
4880}
4881
4882sub getAffectedMethods($$$)
4883{
4884 my ($Level, $Target_TypeName, $Kinds_Locations) = @_;
4885
4886 my $LIMIT = 10;
4887 if(defined $AffectLimit) {
4888 $LIMIT = $AffectLimit;
4889 }
4890
4891 my @Kinds = sort keys(%{$Kinds_Locations});
4892 my %KLocs = ();
4893 foreach my $Kind (@Kinds)
4894 {
4895 my @Locs = sort {$a=~/retval/ cmp $b=~/retval/} sort {length($a)<=>length($b)} sort keys(%{$Kinds_Locations->{$Kind}});
4896 $KLocs{$Kind} = \@Locs;
4897 }
4898
4899 my %SymLocKind = ();
4900 foreach my $Method (sort keys(%{$TypeProblemsIndex{$Target_TypeName}}))
4901 {
4902 if($Method eq ".client_method") {
4903 next;
4904 }
4905
4906 foreach my $Kind (@Kinds)
4907 {
4908 foreach my $Loc (@{$KLocs{$Kind}})
4909 {
4910 if(not defined $CompatProblems{$Method}{$Kind}{$Loc}) {
4911 next;
4912 }
4913
4914 my $Type_Name = $CompatProblems{$Method}{$Kind}{$Loc}{"Type_Name"};
4915 if($Type_Name ne $Target_TypeName) {
4916 next;
4917 }
4918
4919 $SymLocKind{$Method}{$Loc}{$Kind} = 1;
4920 last;
4921 }
4922 }
4923 }
4924
4925 %KLocs = (); # clear
4926
4927 if(not keys(%SymLocKind)) {
4928 return "";
4929 }
4930
4931 my %SymSel = ();
4932 my $Num = 0;
4933 foreach my $Method (sort keys(%SymLocKind))
4934 {
4935 LOOP: foreach my $Loc (sort {$a=~/retval/ cmp $b=~/retval/} sort {length($a)<=>length($b)} sort keys(%{$SymLocKind{$Method}}))
4936 {
4937 foreach my $Kind (sort keys(%{$SymLocKind{$Method}{$Loc}}))
4938 {
4939 $SymSel{$Method}{"Loc"} = $Loc;
4940 $SymSel{$Method}{"Kind"} = $Kind;
4941 last LOOP;
4942 }
4943 }
4944
4945 $Num += 1;
4946
4947 if($Num>=$LIMIT) {
4948 last;
4949 }
4950 }
4951
4952 my $Affected = "";
4953
4954 foreach my $Method (sort {lc($a) cmp lc($b)} keys(%SymSel))
4955 {
4956 my $Kind = $SymSel{$Method}{"Kind"};
4957 my $Loc = $SymSel{$Method}{"Loc"};
4958
4959 my $Desc = getAffectDesc($Method, $Kind, $Loc, $Level);
4960 my $PName = getParamName($Loc);
4961 my $Pos = getParamPos($PName, $Method, 1);
4962
4963 $Affected .= "<span class='iname_a'>".get_Signature($Method, 1, "HTML|Italic|Param|Class|Target=".$Pos)."</span><br/>";
4964 $Affected .= "<div class='affect'>".$Desc."</div>\n";
4965 }
4966
4967 if(keys(%SymLocKind)>$LIMIT) {
4968 $Affected .= " <b>...</b>\n<br/>\n"; # and others ...
4969 }
4970
4971 $Affected = "<div class='affected'>".$Affected."</div>";
4972 if($Affected)
4973 {
4974 my $Num = keys(%SymLocKind);
4975 my $Per = show_number($Num*100/keys(%CheckedMethods));
4976 $Affected = $ContentDivStart.$Affected.$ContentDivEnd;
4977 $Affected = $ContentSpanStart_Affected."[+] affected methods: $Num ($Per\%)".$ContentSpanEnd.$Affected;
4978 }
4979
4980 return ($Affected);
4981}
4982
4983sub getAffectDesc($$$$)
4984{
4985 my ($Method, $Kind, $Location, $Level) = @_;
4986 my %Affect = %{$CompatProblems{$Method}{$Kind}{$Location}};
4987 my $New_Value = $Affect{"New_Value"};
4988 my $Type_Name = $Affect{"Type_Name"};
4989 my @Sentence_Parts = ();
4990
4991 $Location=~s/\.[^.]+?\Z//;
4992
4993 my %TypeAttr = get_Type($MethodInfo{1}{$Method}{"Class"}, 1);
4994 my $Type_Type = $TypeAttr{"Type"};
4995
4996 my $ABSTRACT_M = $MethodInfo{1}{$Method}{"Abstract"}?" abstract":"";
4997 my $ABSTRACT_C = $TypeAttr{"Abstract"}?" abstract":"";
4998 my $METHOD_TYPE = $MethodInfo{1}{$Method}{"Constructor"}?"constructor":"method";
4999
5000 if($Kind eq "Class_Overridden_Method" or $Kind eq "Class_Method_Moved_Up_Hierarchy") {
5001 return "Method '".highLight_Signature($New_Value, 2)."' will be called instead of this method in a client program.";
5002 }
5003 elsif($TypeProblems_Kind{$Level}{$Kind})
5004 {
5005 my %MInfo = %{$MethodInfo{1}{$Method}};
5006
5007 if($Location eq "this") {
5008 return "This$ABSTRACT_M $METHOD_TYPE is from \'".htmlSpecChars($Type_Name)."\'$ABSTRACT_C $Type_Type.";
5009 }
5010
5011 my $TypeID = undef;
5012
5013 if($Location=~/retval/)
5014 { # return value
5015 if($Location=~/\./) {
5016 push(@Sentence_Parts, "Field \'".htmlSpecChars($Location)."\' in return value");
5017 }
5018 else {
5019 push(@Sentence_Parts, "Return value");
5020 }
5021
5022 $TypeID = $MInfo{"Return"};
5023 }
5024 elsif($Location=~/this/)
5025 { # "this" reference
5026 push(@Sentence_Parts, "Field \'".htmlSpecChars($Location)."\' in the object");
5027
5028 $TypeID = $MInfo{"Class"};
5029 }
5030 else
5031 { # parameters
5032 my $PName = getParamName($Location);
5033 my $PPos = getParamPos($PName, $Method, 1);
5034
5035 if($Location=~/\./) {
5036 push(@Sentence_Parts, "Field \'".htmlSpecChars($Location)."\' in ".showPos($PPos)." parameter");
5037 }
5038 else {
5039 push(@Sentence_Parts, showPos($PPos)." parameter");
5040 }
5041 if($PName) {
5042 push(@Sentence_Parts, "\'$PName\'");
5043 }
5044
5045 if(defined $MInfo{"Param"}) {
5046 $TypeID = $MInfo{"Param"}{$PPos}{"Type"};
5047 }
5048 }
5049 push(@Sentence_Parts, " of this$ABSTRACT_M method");
5050
5051 my $Location_T = $Location;
5052 $Location_T=~s/\A\w+(\.|\Z)//; # location in type
5053
5054 my $TypeID_Problem = $TypeID;
5055 if($Location_T) {
5056 $TypeID_Problem = getFieldType($Location_T, $TypeID, 1);
5057 }
5058
5059 if($TypeInfo{1}{$TypeID_Problem}{"Name"} eq $Type_Name) {
5060 push(@Sentence_Parts, "has type \'".htmlSpecChars($Type_Name)."\'.");
5061 }
5062 else {
5063 push(@Sentence_Parts, "has base type \'".htmlSpecChars($Type_Name)."\'.");
5064 }
5065 }
5066 return join(" ", @Sentence_Parts);
5067}
5068
5069sub getParamPos($$$)
5070{
5071 my ($Name, $Method, $LibVersion) = @_;
5072
5073 if(defined $MethodInfo{$LibVersion}{$Method}
5074 and defined $MethodInfo{$LibVersion}{$Method}{"Param"})
5075 {
5076 my $Info = $MethodInfo{$LibVersion}{$Method};
5077 foreach (keys(%{$Info->{"Param"}}))
5078 {
5079 if($Info->{"Param"}{$_}{"Name"} eq $Name)
5080 {
5081 return $_;
5082 }
5083 }
5084 }
5085
5086 return undef;
5087}
5088
5089sub getParamName($)
5090{
5091 my $Loc = $_[0];
5092 $Loc=~s/\..*//g;
5093 return $Loc;
5094}
5095
5096sub getFieldType($$$)
5097{
5098 my ($Location, $TypeId, $LibVersion) = @_;
5099
5100 my @Fields = split(/\./, $Location);
5101
5102 foreach my $Name (@Fields)
5103 {
5104 my %Info = get_BaseType($TypeId, $LibVersion);
5105
5106 foreach my $N (keys(%{$Info{"Fields"}}))
5107 {
5108 if($N eq $Name)
5109 {
5110 $TypeId = $Info{"Fields"}{$N}{"Type"};
5111 last;
5112 }
5113 }
5114 }
5115
5116 return $TypeId;
5117}
5118
5119sub writeReport($$)
5120{
5121 my ($Level, $Report) = @_;
5122 my $RPath = getReportPath($Level);
5123 writeFile($RPath, $Report);
5124}
5125
5126sub getRelPath($$)
5127{
5128 my ($A, $B) = @_;
5129 return abs2rel($A, get_dirname($B));
5130}
5131
5132sub createReport()
5133{
5134 if($JoinReport)
5135 { # --stdout
5136 writeReport("Join", getReport("Join"));
5137 }
5138 elsif($DoubleReport)
5139 { # default
5140 writeReport("Binary", getReport("Binary"));
5141 writeReport("Source", getReport("Source"));
5142 }
5143 elsif($BinaryOnly)
5144 { # --binary
5145 writeReport("Binary", getReport("Binary"));
5146 }
5147 elsif($SourceOnly)
5148 { # --source
5149 writeReport("Source", getReport("Source"));
5150 }
5151}
5152
5153sub getCssStyles()
5154{
5155 my $CssStyles = "
5156 body {
5157 font-family:Arial, sans-serif;
5158 background-color:White;
5159 color:Black;
5160 }
5161 hr {
5162 color:Black;
5163 background-color:Black;
5164 height:1px;
5165 border:0;
5166 }
5167 h1 {
5168 margin-bottom:0px;
5169 padding-bottom:0px;
5170 font-size:1.625em;
5171 }
5172 h2 {
5173 margin-bottom:0px;
5174 padding-bottom:0px;
5175 font-size:1.25em;
5176 white-space:nowrap;
5177 }
5178 div.symbols {
5179 color:#003E69;
5180 }
5181 div.symbols i {
5182 color:Brown;
5183 }
5184 span.section {
5185 font-weight:bold;
5186 cursor:pointer;
5187 color:#003E69;
5188 white-space:nowrap;
5189 margin-left:5px;
5190 }
5191 span:hover.section {
5192 color:#336699;
5193 }
5194 span.sect_aff {
5195 cursor:pointer;
5196 margin-left:7px;
5197 padding-left:15px;
5198 font-size:0.875em;
5199 color:#cc3300;
5200 }
5201 span.ext {
5202 font-weight:100;
5203 }
5204 span.jar {
5205 color:#cc3300;
5206 font-size:0.875em;
5207 font-weight:bold;
5208 }
5209 div.jar_list {
5210 padding-left:5px;
5211 font-size:0.94em;
5212 }
5213 span.pkg_t {
5214 color:#408080;
5215 font-size:0.875em;
5216 }
5217 span.pkg {
5218 color:#408080;
5219 font-size:0.875em;
5220 font-weight:bold;
5221 }
5222 span.cname {
5223 color:Green;
5224 font-size:0.875em;
5225 font-weight:bold;
5226 }
5227 span.iname_b {
5228 font-weight:bold;
5229 font-size:1.1em;
5230 }
5231 span.iname_a {
5232 color:#333333;
5233 font-weight:bold;
5234 font-size:0.94em;
5235 }
5236 span.sym_p {
5237 font-weight:normal;
5238 white-space:normal;
5239 }
5240 span.sym_p span {
5241 white-space:nowrap;
5242 }
5243 span.attr {
5244 color:Black;
5245 font-weight:100;
5246 }
5247 span.deprecated {
5248 color:Red;
5249 font-weight:bold;
5250 font-family:Monaco, monospace;
5251 }
5252 div.affect {
5253 padding-left:15px;
5254 padding-bottom:10px;
5255 font-size:0.87em;
5256 font-style:italic;
5257 line-height:0.75em;
5258 }
5259 div.affected {
5260 padding-left:30px;
5261 padding-top:10px;
5262 }
5263 table.ptable {
5264 border-collapse:collapse;
5265 border:1px outset black;
5266 line-height:1em;
5267 margin-left:15px;
5268 margin-top:3px;
5269 margin-bottom:3px;
5270 width:900px;
5271 }
5272 table.ptable td {
5273 border:1px solid Gray;
5274 padding: 3px;
5275 font-size:0.875em;
5276 text-align:left;
5277 vertical-align:top;
5278 }
5279 table.ptable th {
5280 background-color:#eeeeee;
5281 font-weight:bold;
5282 color:#333333;
5283 font-family:Verdana, Arial;
5284 font-size:0.875em;
5285 border:1px solid Gray;
5286 text-align:center;
5287 vertical-align:top;
5288 white-space:nowrap;
5289 padding: 3px;
5290 }
5291 table.summary {
5292 border-collapse:collapse;
5293 border:1px outset black;
5294 }
5295 table.summary th {
5296 background-color:#eeeeee;
5297 font-weight:100;
5298 text-align:left;
5299 font-size:0.94em;
5300 white-space:nowrap;
5301 border:1px inset Gray;
5302 padding: 3px;
5303 }
5304 table.summary td {
5305 text-align:right;
5306 white-space:nowrap;
5307 border:1px inset Gray;
5308 padding: 3px 5px 3px 10px;
5309 }
5310 span.mngl {
5311 padding-left:15px;
5312 font-size:0.875em;
5313 cursor:text;
5314 color:#444444;
5315 }
5316 span.color_p {
5317 font-style:italic;
5318 color:Brown;
5319 }
5320 span.param {
5321 font-style:italic;
5322 }
5323 span.focus_p {
5324 font-style:italic;
5325 background-color:#DCDCDC;
5326 }
5327 span.nowrap {
5328 white-space:nowrap;
5329 }
5330 .passed {
5331 background-color:#CCFFCC;
5332 font-weight:100;
5333 }
5334 .warning {
5335 background-color:#F4F4AF;
5336 font-weight:100;
5337 }
5338 .failed {
5339 background-color:#FFCCCC;
5340 font-weight:100;
5341 }
5342 .new {
5343 background-color:#C6DEFF;
5344 font-weight:100;
5345 }
5346
5347 .compatible {
5348 background-color:#CCFFCC;
5349 font-weight:100;
5350 }
5351 .almost_compatible {
5352 background-color:#FFDAA3;
5353 font-weight:100;
5354 }
5355 .incompatible {
5356 background-color:#FFCCCC;
5357 font-weight:100;
5358 }
5359 .gray {
5360 background-color:#DCDCDC;
5361 font-weight:100;
5362 }
5363
5364 .top_ref {
5365 font-size:0.69em;
5366 }
5367 .footer {
5368 font-size:0.75em;
5369 }";
5370
5371 if($JoinReport or $ExternCss)
5372 {
5373 $CssStyles .= "
5374 .tabset {
5375 float:left;
5376 }
5377 a.tab {
5378 border:1px solid Black;
5379 float:left;
5380 margin:0px 5px -1px 0px;
5381 padding:3px 5px 3px 5px;
5382 position:relative;
5383 font-size:0.875em;
5384 background-color:#DDD;
5385 text-decoration:none;
5386 color:Black;
5387 }
5388 a.disabled:hover
5389 {
5390 color:Black;
5391 background:#EEE;
5392 }
5393 a.active:hover
5394 {
5395 color:Black;
5396 background:White;
5397 }
5398 a.active {
5399 border-bottom-color:White;
5400 background-color:White;
5401 }
5402 div.tab {
5403 border-top:1px solid Black;
5404 padding:0px;
5405 width:100%;
5406 clear:both;
5407 }";
5408 }
5409
5410 return $CssStyles;
5411}
5412
5413sub getJsScript()
5414{
5415 my $JScripts = "
5416 function sC(header, id)
5417 {
5418 e = document.getElementById(id);
5419 if(e.style.display == 'none')
5420 {
5421 e.style.display = 'block';
5422 e.style.visibility = 'visible';
5423 header.innerHTML = header.innerHTML.replace(/\\\[[^0-9 ]\\\]/gi,\"[&minus;]\");
5424 }
5425 else
5426 {
5427 e.style.display = 'none';
5428 e.style.visibility = 'hidden';
5429 header.innerHTML = header.innerHTML.replace(/\\\[[^0-9 ]\\\]/gi,\"[+]\");
5430 }
5431 }";
5432
5433 if($JoinReport or $ExternJs)
5434 {
5435 $JScripts .= "
5436 function initTabs()
5437 {
5438 var url = window.location.href;
5439 if(url.indexOf('_Source_')!=-1 || url.indexOf('#Source')!=-1)
5440 {
5441 var tab1 = document.getElementById('BinaryID');
5442 var tab2 = document.getElementById('SourceID');
5443 tab1.className='tab disabled';
5444 tab2.className='tab active';
5445 }
5446 var sets = document.getElementsByTagName('div');
5447 for (var i = 0; i < sets.length; i++)
5448 {
5449 if (sets[i].className.indexOf('tabset') != -1)
5450 {
5451 var tabs = [];
5452 var links = sets[i].getElementsByTagName('a');
5453 for (var j = 0; j < links.length; j++)
5454 {
5455 if (links[j].className.indexOf('tab') != -1)
5456 {
5457 tabs.push(links[j]);
5458 links[j].tabs = tabs;
5459 var tab = document.getElementById(links[j].href.substr(links[j].href.indexOf('#') + 1));
5460 //reset all tabs on start
5461 if (tab)
5462 {
5463 if (links[j].className.indexOf('active')!=-1) {
5464 tab.style.display = 'block';
5465 }
5466 else {
5467 tab.style.display = 'none';
5468 }
5469 }
5470 links[j].onclick = function()
5471 {
5472 var tab = document.getElementById(this.href.substr(this.href.indexOf('#') + 1));
5473 if (tab)
5474 {
5475 //reset all tabs before change
5476 for (var k = 0; k < this.tabs.length; k++)
5477 {
5478 document.getElementById(this.tabs[k].href.substr(this.tabs[k].href.indexOf('#') + 1)).style.display = 'none';
5479 this.tabs[k].className = this.tabs[k].className.replace('active', 'disabled');
5480 }
5481 this.className = 'tab active';
5482 tab.style.display = 'block';
5483 // window.location.hash = this.id.replace('ID', '');
5484 return false;
5485 }
5486 }
5487 }
5488 }
5489 }
5490 }
5491 if(url.indexOf('#')!=-1) {
5492 location.href=location.href;
5493 }
5494 }
5495 if (window.addEventListener) window.addEventListener('load', initTabs, false);
5496 else if (window.attachEvent) window.attachEvent('onload', initTabs);";
5497 }
5498
5499 return $JScripts;
5500}
5501
5502sub getReport($)
5503{
5504 my $Level = $_[0];
5505
5506 my $CssStyles = getCssStyles();
5507 my $JScripts = getJsScript();
5508
5509 if(defined $ExternCss)
5510 {
5511 $CssStyles=~s/\n /\n/g;
5512 writeFile($ExternCss, $CssStyles);
5513 }
5514
5515 if(defined $ExternJs)
5516 {
5517 $JScripts=~s/\n /\n/g;
5518 writeFile($ExternJs, $JScripts);
5519 }
5520
5521 if($Level eq "Join")
5522 {
5523 my $Title = "$TargetTitle: ".$Descriptor{1}{"Version"}." to ".$Descriptor{2}{"Version"}." compatibility report";
5524 my $Keywords = "$TargetTitle, compatibility";
5525 my $Description = "Compatibility report for the $TargetTitle library between ".$Descriptor{1}{"Version"}." and ".$Descriptor{2}{"Version"}." versions";
5526 my ($BSummary, $BMetaData) = get_Summary("Binary");
5527 my ($SSummary, $SMetaData) = get_Summary("Source");
5528 my $Report = "<!-\- $BMetaData -\->\n<!-\- $SMetaData -\->\n".composeHTML_Head($Level, $Title, $Keywords, $Description, $CssStyles, $JScripts)."<body><a name='Source'></a><a name='Binary'></a><a name='Top'></a>";
5529 $Report .= get_Report_Header("Join")."
5530 <br/><div class='tabset'>
5531 <a id='BinaryID' href='#BinaryTab' class='tab active'>Binary<br/>Compatibility</a>
5532 <a id='SourceID' href='#SourceTab' style='margin-left:3px' class='tab disabled'>Source<br/>Compatibility</a>
5533 </div>";
5534 $Report .= "<div id='BinaryTab' class='tab'>\n$BSummary\n".get_Report_Added("Binary").get_Report_Removed("Binary").get_Report_Problems("High", "Binary").get_Report_Problems("Medium", "Binary").get_Report_Problems("Low", "Binary").get_Report_Problems("Safe", "Binary").get_SourceInfo()."<br/><br/><br/></div>";
5535 $Report .= "<div id='SourceTab' class='tab'>\n$SSummary\n".get_Report_Added("Source").get_Report_Removed("Source").get_Report_Problems("High", "Source").get_Report_Problems("Medium", "Source").get_Report_Problems("Low", "Source").get_Report_Problems("Safe", "Source").get_SourceInfo()."<br/><br/><br/></div>";
5536 $Report .= getReportFooter();
5537 $Report .= "\n</body></html>";
5538 return $Report;
5539 }
5540 else
5541 {
5542 my ($Summary, $MetaData) = get_Summary($Level);
5543 my $Title = "$TargetTitle: ".$Descriptor{1}{"Version"}." to ".$Descriptor{2}{"Version"}." ".lc($Level)." compatibility report";
5544 my $Keywords = "$TargetTitle, ".lc($Level).", compatibility";
5545 my $Description = "$Level compatibility report for the $TargetTitle library between ".$Descriptor{1}{"Version"}." and ".$Descriptor{2}{"Version"}." versions";
5546
5547 my $Report = "<!-\- $MetaData -\->\n".composeHTML_Head($Level, $Title, $Keywords, $Description, $CssStyles, $JScripts)."<body><a name='Top'></a>";
5548 $Report .= get_Report_Header($Level)."\n".$Summary."\n";
5549 $Report .= get_Report_Added($Level).get_Report_Removed($Level);
5550 $Report .= get_Report_Problems("High", $Level).get_Report_Problems("Medium", $Level).get_Report_Problems("Low", $Level).get_Report_Problems("Safe", $Level);
5551 $Report .= get_SourceInfo()."<br/><br/><br/>\n";
5552 $Report .= getReportFooter();
5553 $Report .= "\n</body></html>";
5554 return $Report;
5555 }
5556}
5557
5558sub getReportFooter()
5559{
5560 my $Footer = "";
5561 $Footer .= "<hr/>";
5562 $Footer .= "<div class='footer' align='right'><i>Generated by ";
5563 $Footer .= "<a href='".$HomePage{"Dev"}."'>Java API Compliance Checker</a> $TOOL_VERSION &#160;";
5564 $Footer .= "</i></div>";
5565 $Footer .= "<br/>";
5566 return $Footer;
5567}
5568
5569sub get_Report_Problems($$)
5570{
5571 my ($Priority, $Level) = @_;
5572 my $Report = get_Report_TypeProblems($Priority, $Level);
5573 if(my $MProblems = get_Report_MethodProblems($Priority, $Level)) {
5574 $Report .= $MProblems;
5575 }
5576 if($Report)
5577 {
5578 if($JoinReport)
5579 {
5580 if($Priority eq "Safe") {
5581 $Report = "<a name=\'Other_".$Level."_Changes\'></a>".$Report;
5582 }
5583 else {
5584 $Report = "<a name=\'".$Priority."_Risk_".$Level."_Problems\'></a>".$Report;
5585 }
5586 }
5587 else
5588 {
5589 if($Priority eq "Safe") {
5590 $Report = "<a name=\'Other_Changes\'></a>".$Report;
5591 }
5592 else {
5593 $Report = "<a name=\'".$Priority."_Risk_Problems\'></a>".$Report;
5594 }
5595 }
5596 }
5597 return $Report;
5598}
5599
5600sub composeHTML_Head($$$$$$)
5601{
5602 my ($Level, $Title, $Keywords, $Description, $Styles, $Scripts) = @_;
5603
5604 my $Head = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
5605 $Head .= "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n";
5606 $Head .= "<head>\n";
5607 $Head .= "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
5608 $Head .= "<meta name=\"keywords\" content=\"$Keywords\" />\n";
5609 $Head .= "<meta name=\"description\" content=\"$Description\" />\n";
5610
5611 my $RPath = getReportPath($Level);
5612
5613 if(defined $ExternCss) {
5614 $Head .= "<link rel=\"stylesheet\" type=\"text/css\" href=\"".getRelPath($ExternCss, $RPath)."\" />\n";
5615 }
5616
5617 if(defined $ExternJs) {
5618 $Head .= "<script type=\"text/javascript\" src=\"".getRelPath($ExternJs, $RPath)."\"></script>\n";
5619 }
5620
5621 $Head .= "<title>$Title</title>\n";
5622
5623 if(not defined $ExternCss) {
5624 $Head .= "<style type=\"text/css\">\n$Styles\n</style>\n";
5625 }
5626
5627 if(not defined $ExternJs) {
5628 $Head .= "<script type=\"text/javascript\" language=\"JavaScript\">\n<!--\n$Scripts\n-->\n</script>\n";
5629 }
5630
5631 $Head .= "</head>\n";
5632
5633 $Head=~s/\n /\n/g;
5634
5635 return $Head;
5636}
5637
5638sub insertIDs($)
5639{
5640 my $Text = $_[0];
5641 while($Text=~/CONTENT_ID/)
5642 {
5643 if(int($Content_Counter)%2)
5644 {
5645 $ContentID -= 1;
5646 }
5647 $Text=~s/CONTENT_ID/c_$ContentID/;
5648 $ContentID += 1;
5649 $Content_Counter += 1;
5650 }
5651 return $Text;
5652}
5653
5654sub readArchives($)
5655{
5656 my $LibVersion = $_[0];
5657 my @ArchivePaths = getArchives($LibVersion);
5658 if($#ArchivePaths==-1) {
5659 exitStatus("Error", "Java ARchives are not found in ".$Descriptor{$LibVersion}{"Version"});
5660 }
5661 printMsg("INFO", "reading classes ".$Descriptor{$LibVersion}{"Version"}." ...");
5662 $TYPE_ID = 0;
5663
5664 foreach my $ArchivePath (sort {length($a)<=>length($b)} @ArchivePaths) {
5665 readArchive($LibVersion, $ArchivePath);
5666 }
5667 foreach my $TName (keys(%{$TName_Tid{$LibVersion}}))
5668 {
5669 my $Tid = $TName_Tid{$LibVersion}{$TName};
5670 if(not $TypeInfo{$LibVersion}{$Tid}{"Type"})
5671 {
5672 if($TName=~/\A(void|boolean|char|byte|short|int|float|long|double)\Z/) {
5673 $TypeInfo{$LibVersion}{$Tid}{"Type"} = "primitive";
5674 }
5675 else {
5676 $TypeInfo{$LibVersion}{$Tid}{"Type"} = "class";
5677 }
5678 }
5679 }
5680
5681 foreach my $Method (keys(%{$MethodInfo{$LibVersion}}))
5682 {
5683 $MethodInfo{$LibVersion}{$Method}{"Signature"} = get_Signature($Method, $LibVersion, "Full");
5684 $tr_name{$Method} = get_TypeName($MethodInfo{$LibVersion}{$Method}{"Class"}, $LibVersion).".".get_Signature($Method, $LibVersion, "Short");
5685 }
5686}
5687
5688sub testSystem()
5689{
5690 printMsg("INFO", "\nverifying detectable Java library changes");
5691
5692 my $LibName = "libsample_java";
5693 if(-d $LibName) {
5694 rmtree($LibName);
5695 }
5696
5697 my $PackageName = "TestPackage";
5698 my $Path_v1 = "$LibName/$PackageName.v1/$PackageName";
5699 mkpath($Path_v1);
5700
5701 my $Path_v2 = "$LibName/$PackageName.v2/$PackageName";
5702 mkpath($Path_v2);
5703
5704 my $TestsPath = "$LibName/Tests";
5705 mkpath($TestsPath);
5706
5707 # FirstCheckedException
5708 my $FirstCheckedException = "package $PackageName;
5709 public class FirstCheckedException extends Exception {
5710 }";
5711 writeFile($Path_v1."/FirstCheckedException.java", $FirstCheckedException);
5712 writeFile($Path_v2."/FirstCheckedException.java", $FirstCheckedException);
5713
5714 # SecondCheckedException
5715 my $SecondCheckedException = "package $PackageName;
5716 public class SecondCheckedException extends Exception {
5717 }";
5718 writeFile($Path_v1."/SecondCheckedException.java", $SecondCheckedException);
5719 writeFile($Path_v2."/SecondCheckedException.java", $SecondCheckedException);
5720
5721 # FirstUncheckedException
5722 my $FirstUncheckedException = "package $PackageName;
5723 public class FirstUncheckedException extends RuntimeException {
5724 }";
5725 writeFile($Path_v1."/FirstUncheckedException.java", $FirstUncheckedException);
5726 writeFile($Path_v2."/FirstUncheckedException.java", $FirstUncheckedException);
5727
5728 # SecondUncheckedException
5729 my $SecondUncheckedException = "package $PackageName;
5730 public class SecondUncheckedException extends RuntimeException {
5731 }";
5732 writeFile($Path_v1."/SecondUncheckedException.java", $SecondUncheckedException);
5733 writeFile($Path_v2."/SecondUncheckedException.java", $SecondUncheckedException);
5734
5735 # BaseAbstractClass
5736 my $BaseAbstractClass = "package $PackageName;
5737 public abstract class BaseAbstractClass {
5738 public Integer field;
5739 public Integer someMethod(Integer param) { return param; }
5740 public abstract Integer abstractMethod(Integer param);
5741 }";
5742 writeFile($Path_v1."/BaseAbstractClass.java", $BaseAbstractClass);
5743 writeFile($Path_v2."/BaseAbstractClass.java", $BaseAbstractClass);
5744
5745 # Removed_Annotation
5746 writeFile($Path_v1."/RemovedAnnotation.java",
5747 "package $PackageName;
5748 public \@interface RemovedAnnotation {
5749 }");
5750
5751 # Beta Annotation
5752 writeFile($Path_v1."/Beta.java",
5753 "package $PackageName;
5754 public \@interface Beta {
5755 }");
5756
5757 writeFile($Path_v2."/Beta.java",
5758 "package $PackageName;
5759 public \@interface Beta {
5760 }");
5761
5762 # BaseClass
5763 my $BaseClass = "package $PackageName;
5764 public class BaseClass {
5765 public Integer field;
5766 public Integer method(Integer param) { return param; }
5767 }";
5768 writeFile($Path_v1."/BaseClass.java", $BaseClass);
5769 writeFile($Path_v2."/BaseClass.java", $BaseClass);
5770
5771 # BaseClass2
5772 my $BaseClass2 = "package $PackageName;
5773 public class BaseClass2 {
5774 public Integer field2;
5775 public Integer method2(Integer param) { return param; }
5776 }";
5777 writeFile($Path_v1."/BaseClass2.java", $BaseClass2);
5778 writeFile($Path_v2."/BaseClass2.java", $BaseClass2);
5779
5780 # BaseInterface
5781 my $BaseInterface = "package $PackageName;
5782 public interface BaseInterface {
5783 public Integer field = 100;
5784 public Integer method(Integer param);
5785 }";
5786 writeFile($Path_v1."/BaseInterface.java", $BaseInterface);
5787 writeFile($Path_v2."/BaseInterface.java", $BaseInterface);
5788
5789 # BaseInterface2
5790 my $BaseInterface2 = "package $PackageName;
5791 public interface BaseInterface2 {
5792 public Integer field2 = 100;
5793 public Integer method2(Integer param);
5794 }";
5795 writeFile($Path_v1."/BaseInterface2.java", $BaseInterface2);
5796 writeFile($Path_v2."/BaseInterface2.java", $BaseInterface2);
5797
5798 # BaseConstantInterface
5799 my $BaseConstantInterface = "package $PackageName;
5800 public interface BaseConstantInterface {
5801 public Integer CONSTANT = 10;
5802 public Integer CONSTANT2 = 100;
5803 }";
5804 writeFile($Path_v1."/BaseConstantInterface.java", $BaseConstantInterface);
5805 writeFile($Path_v2."/BaseConstantInterface.java", $BaseConstantInterface);
5806
5807 # Removed_Method (Beta method)
5808 writeFile($Path_v1."/RemovedBetaMethod.java",
5809 "package $PackageName;
5810 public class RemovedBetaMethod
5811 {
5812 \@Beta
5813 public Integer someMethod() {
5814 return 0;
5815 }
5816 }");
5817 writeFile($Path_v2."/RemovedBetaMethod.java",
5818 "package $PackageName;
5819 public class RemovedBetaMethod {
5820 }");
5821
5822 # Removed_Method (from Beta class)
5823 writeFile($Path_v1."/RemovedMethodFromBetaClass.java",
5824 "package $PackageName;
5825 \@Beta
5826 public class RemovedMethodFromBetaClass
5827 {
5828 public Integer someMethod() {
5829 return 0;
5830 }
5831 }");
5832 writeFile($Path_v2."/RemovedMethodFromBetaClass.java",
5833 "package $PackageName;
5834 \@Beta
5835 public class RemovedMethodFromBetaClass {
5836 }");
5837
5838 # Removed_Class (Beta)
5839 writeFile($Path_v1."/RemovedBetaClass.java",
5840 "package $PackageName;
5841 \@Beta
5842 public class RemovedBetaClass
5843 {
5844 public Integer someMethod() {
5845 return 0;
5846 }
5847 }");
5848
5849 # Abstract_Method_Added_Checked_Exception
5850 writeFile($Path_v1."/AbstractMethodAddedCheckedException.java",
5851 "package $PackageName;
5852 public abstract class AbstractMethodAddedCheckedException {
5853 public abstract Integer someMethod() throws FirstCheckedException;
5854 }");
5855 writeFile($Path_v2."/AbstractMethodAddedCheckedException.java",
5856 "package $PackageName;
5857 public abstract class AbstractMethodAddedCheckedException {
5858 public abstract Integer someMethod() throws FirstCheckedException, SecondCheckedException;
5859 }");
5860
5861 # Abstract_Method_Removed_Checked_Exception
5862 writeFile($Path_v1."/AbstractMethodRemovedCheckedException.java",
5863 "package $PackageName;
5864 public abstract class AbstractMethodRemovedCheckedException {
5865 public abstract Integer someMethod() throws FirstCheckedException, SecondCheckedException;
5866 }");
5867 writeFile($Path_v2."/AbstractMethodRemovedCheckedException.java",
5868 "package $PackageName;
5869 public abstract class AbstractMethodRemovedCheckedException {
5870 public abstract Integer someMethod() throws FirstCheckedException;
5871 }");
5872
5873 # NonAbstract_Method_Added_Checked_Exception
5874 writeFile($Path_v1."/NonAbstractMethodAddedCheckedException.java",
5875 "package $PackageName;
5876 public class NonAbstractMethodAddedCheckedException {
5877 public Integer someMethod() throws FirstCheckedException {
5878 return 10;
5879 }
5880 }");
5881 writeFile($Path_v2."/NonAbstractMethodAddedCheckedException.java",
5882 "package $PackageName;
5883 public class NonAbstractMethodAddedCheckedException {
5884 public Integer someMethod() throws FirstCheckedException, SecondCheckedException {
5885 return 10;
5886 }
5887 }");
5888
5889 # NonAbstract_Method_Removed_Checked_Exception
5890 writeFile($Path_v1."/NonAbstractMethodRemovedCheckedException.java",
5891 "package $PackageName;
5892 public class NonAbstractMethodRemovedCheckedException {
5893 public Integer someMethod() throws FirstCheckedException, SecondCheckedException {
5894 return 10;
5895 }
5896 }");
5897 writeFile($Path_v2."/NonAbstractMethodRemovedCheckedException.java",
5898 "package $PackageName;
5899 public class NonAbstractMethodRemovedCheckedException {
5900 public Integer someMethod() throws FirstCheckedException {
5901 return 10;
5902 }
5903 }");
5904
5905 # Added_Unchecked_Exception
5906 writeFile($Path_v1."/AddedUncheckedException.java",
5907 "package $PackageName;
5908 public class AddedUncheckedException {
5909 public Integer someMethod() throws FirstUncheckedException {
5910 return 10;
5911 }
5912 }");
5913 writeFile($Path_v2."/AddedUncheckedException.java",
5914 "package $PackageName;
5915 public class AddedUncheckedException {
5916 public Integer someMethod() throws FirstUncheckedException, SecondUncheckedException, NullPointerException {
5917 return 10;
5918 }
5919 }");
5920
5921 # Removed_Unchecked_Exception
5922 writeFile($Path_v1."/RemovedUncheckedException.java",
5923 "package $PackageName;
5924 public class RemovedUncheckedException {
5925 public Integer someMethod() throws FirstUncheckedException, SecondUncheckedException, NullPointerException {
5926 return 10;
5927 }
5928 }");
5929 writeFile($Path_v2."/RemovedUncheckedException.java",
5930 "package $PackageName;
5931 public class RemovedUncheckedException {
5932 public Integer someMethod() throws FirstUncheckedException {
5933 return 10;
5934 }
5935 }");
5936
5937 # Changed_Method_Return_From_Void
5938 writeFile($Path_v1."/ChangedMethodReturnFromVoid.java",
5939 "package $PackageName;
5940 public class ChangedMethodReturnFromVoid {
5941 public void changedMethod(Integer param1, String[] param2) { }
5942 }");
5943 writeFile($Path_v2."/ChangedMethodReturnFromVoid.java",
5944 "package $PackageName;
5945 public class ChangedMethodReturnFromVoid {
5946 public Integer changedMethod(Integer param1, String[] param2){
5947 return param1;
5948 }
5949 }");
5950
5951 # Added_Method
5952 writeFile($Path_v1."/AddedMethod.java",
5953 "package $PackageName;
5954 public class AddedMethod {
5955 public Integer field = 100;
5956 }");
5957 writeFile($Path_v2."/AddedMethod.java",
5958 "package $PackageName;
5959 public class AddedMethod {
5960 public Integer field = 100;
5961 public Integer addedMethod(Integer param1, String[] param2) { return param1; }
5962 public static String[] addedStaticMethod(String[] param) { return param; }
5963 }");
5964
5965 # Added_Method (Constructor)
5966 writeFile($Path_v1."/AddedConstructor.java",
5967 "package $PackageName;
5968 public class AddedConstructor {
5969 public Integer field = 100;
5970 }");
5971 writeFile($Path_v2."/AddedConstructor.java",
5972 "package $PackageName;
5973 public class AddedConstructor {
5974 public Integer field = 100;
5975 public AddedConstructor() { }
5976 public AddedConstructor(Integer x, String y) { }
5977 }");
5978
5979 # Class_Added_Field
5980 writeFile($Path_v1."/ClassAddedField.java",
5981 "package $PackageName;
5982 public class ClassAddedField {
5983 public Integer otherField;
5984 }");
5985 writeFile($Path_v2."/ClassAddedField.java",
5986 "package $PackageName;
5987 public class ClassAddedField {
5988 public Integer addedField;
5989 public Integer otherField;
5990 }");
5991
5992 # Interface_Added_Field
5993 writeFile($Path_v1."/InterfaceAddedField.java",
5994 "package $PackageName;
5995 public interface InterfaceAddedField {
5996 public Integer method();
5997 }");
5998 writeFile($Path_v2."/InterfaceAddedField.java",
5999 "package $PackageName;
6000 public interface InterfaceAddedField {
6001 public Integer addedField = 100;
6002 public Integer method();
6003 }");
6004
6005 # Removed_NonConstant_Field (Class)
6006 writeFile($Path_v1."/ClassRemovedField.java",
6007 "package $PackageName;
6008 public class ClassRemovedField {
6009 public Integer removedField;
6010 public Integer otherField;
6011 }");
6012 writeFile($Path_v2."/ClassRemovedField.java",
6013 "package $PackageName;
6014 public class ClassRemovedField {
6015 public Integer otherField;
6016 }");
6017
6018 writeFile($TestsPath."/Test_ClassRemovedField.java",
6019 "import $PackageName.*;
6020 public class Test_ClassRemovedField {
6021 public static void main(String[] args) {
6022 ClassRemovedField X = new ClassRemovedField();
6023 Integer Copy = X.removedField;
6024 }
6025 }");
6026
6027 writeFile($TestsPath."/Test_RemovedAnnotation.java",
6028 "import $PackageName.*;
6029 public class Test_RemovedAnnotation {
6030 public static void main(String[] args) {
6031 testMethod();
6032 }
6033
6034 \@RemovedAnnotation
6035 static void testMethod() {
6036 }
6037 }");
6038
6039 # Removed_Constant_Field (Interface)
6040 writeFile($Path_v1."/InterfaceRemovedConstantField.java",
6041 "package $PackageName;
6042 public interface InterfaceRemovedConstantField {
6043 public String someMethod();
6044 public int removedField_Int = 1000;
6045 public String removedField_Str = \"Value\";
6046 }");
6047 writeFile($Path_v2."/InterfaceRemovedConstantField.java",
6048 "package $PackageName;
6049 public interface InterfaceRemovedConstantField {
6050 public String someMethod();
6051 }");
6052
6053 # Removed_NonConstant_Field (Interface)
6054 writeFile($Path_v1."/InterfaceRemovedField.java",
6055 "package $PackageName;
6056 public interface InterfaceRemovedField {
6057 public String someMethod();
6058 public BaseClass removedField = new BaseClass();
6059 }");
6060 writeFile($Path_v2."/InterfaceRemovedField.java",
6061 "package $PackageName;
6062 public interface InterfaceRemovedField {
6063 public String someMethod();
6064 }");
6065
6066 # Renamed_Field
6067 writeFile($Path_v1."/RenamedField.java",
6068 "package $PackageName;
6069 public class RenamedField {
6070 public String oldName;
6071 }");
6072 writeFile($Path_v2."/RenamedField.java",
6073 "package $PackageName;
6074 public class RenamedField {
6075 public String newName;
6076 }");
6077
6078 # Renamed_Constant_Field
6079 writeFile($Path_v1."/RenamedConstantField.java",
6080 "package $PackageName;
6081 public class RenamedConstantField {
6082 public final String oldName = \"Value\";
6083 }");
6084 writeFile($Path_v2."/RenamedConstantField.java",
6085 "package $PackageName;
6086 public class RenamedConstantField {
6087 public final String newName = \"Value\";
6088 }");
6089
6090 # Changed_Field_Type
6091 writeFile($Path_v1."/ChangedFieldType.java",
6092 "package $PackageName;
6093 public class ChangedFieldType {
6094 public String fieldName;
6095 }");
6096 writeFile($Path_v2."/ChangedFieldType.java",
6097 "package $PackageName;
6098 public class ChangedFieldType {
6099 public Integer fieldName;
6100 }");
6101
6102 # Changed_Field_Access
6103 writeFile($Path_v1."/ChangedFieldAccess.java",
6104 "package $PackageName;
6105 public class ChangedFieldAccess {
6106 public String fieldName;
6107 }");
6108 writeFile($Path_v2."/ChangedFieldAccess.java",
6109 "package $PackageName;
6110 public class ChangedFieldAccess {
6111 private String fieldName;
6112 }");
6113
6114 # Changed_Final_Field_Value
6115 writeFile($Path_v1."/ChangedFinalFieldValue.java",
6116 "package $PackageName;
6117 public class ChangedFinalFieldValue {
6118 public final int field = 1;
6119 public final String field2 = \" \";
6120 }");
6121 writeFile($Path_v2."/ChangedFinalFieldValue.java",
6122 "package $PackageName;
6123 public class ChangedFinalFieldValue {
6124 public final int field = 2;
6125 public final String field2 = \"newValue\";
6126 }");
6127
6128 # NonConstant_Field_Became_Static
6129 writeFile($Path_v1."/NonConstantFieldBecameStatic.java",
6130 "package $PackageName;
6131 public class NonConstantFieldBecameStatic {
6132 public String fieldName;
6133 }");
6134 writeFile($Path_v2."/NonConstantFieldBecameStatic.java",
6135 "package $PackageName;
6136 public class NonConstantFieldBecameStatic {
6137 public static String fieldName;
6138 }");
6139
6140 # NonConstant_Field_Became_NonStatic
6141 writeFile($Path_v1."/NonConstantFieldBecameNonStatic.java",
6142 "package $PackageName;
6143 public class NonConstantFieldBecameNonStatic {
6144 public static String fieldName;
6145 }");
6146 writeFile($Path_v2."/NonConstantFieldBecameNonStatic.java",
6147 "package $PackageName;
6148 public class NonConstantFieldBecameNonStatic {
6149 public String fieldName;
6150 }");
6151
6152 # Constant_Field_Became_NonStatic
6153 writeFile($Path_v1."/ConstantFieldBecameNonStatic.java",
6154 "package $PackageName;
6155 public class ConstantFieldBecameNonStatic {
6156 public final static String fieldName = \"Value\";
6157 }");
6158 writeFile($Path_v2."/ConstantFieldBecameNonStatic.java",
6159 "package $PackageName;
6160 public class ConstantFieldBecameNonStatic {
6161 public final String fieldName = \"Value\";
6162 }");
6163
6164 # Field_Became_Final
6165 writeFile($Path_v1."/FieldBecameFinal.java",
6166 "package $PackageName;
6167 public class FieldBecameFinal {
6168 public String fieldName;
6169 }");
6170 writeFile($Path_v2."/FieldBecameFinal.java",
6171 "package $PackageName;
6172 public class FieldBecameFinal {
6173 public final String fieldName = \"Value\";
6174 }");
6175
6176 # Field_Became_NonFinal
6177 writeFile($Path_v1."/FieldBecameNonFinal.java",
6178 "package $PackageName;
6179 public class FieldBecameNonFinal {
6180 public final String fieldName = \"Value\";
6181 }");
6182 writeFile($Path_v2."/FieldBecameNonFinal.java",
6183 "package $PackageName;
6184 public class FieldBecameNonFinal {
6185 public String fieldName;
6186 }");
6187
6188 # Removed_Method
6189 writeFile($Path_v1."/RemovedMethod.java",
6190 "package $PackageName;
6191 public class RemovedMethod {
6192 public Integer field = 100;
6193 public Integer removedMethod(Integer param1, String param2) { return param1; }
6194 public static Integer removedStaticMethod(Integer param) { return param; }
6195 }");
6196 writeFile($Path_v2."/RemovedMethod.java",
6197 "package $PackageName;
6198 public class RemovedMethod {
6199 public Integer field = 100;
6200 }");
6201
6202 # Removed_Method (move up to java.lang.Object)
6203 writeFile($Path_v1."/MoveUpToJavaLangObject.java",
6204 "package $PackageName;
6205 public class MoveUpToJavaLangObject extends java.lang.Object {
6206 public int hashCode() { return 0; }
6207 }");
6208 writeFile($Path_v2."/MoveUpToJavaLangObject.java",
6209 "package $PackageName;
6210 public class MoveUpToJavaLangObject extends java.lang.Object {
6211 }");
6212
6213 writeFile($TestsPath."/Test_MoveUpToJavaLangObject.java",
6214 "import $PackageName.*;
6215 public class Test_MoveUpToJavaLangObject {
6216 public static void main(String[] args) {
6217 MoveUpToJavaLangObject X = new MoveUpToJavaLangObject();
6218 int R = X.hashCode();
6219 }
6220 }");
6221
6222 # Removed_Method (Deprecated)
6223 writeFile($Path_v1."/RemovedDeprecatedMethod.java",
6224 "package $PackageName;
6225 public class RemovedDeprecatedMethod {
6226 public Integer field = 100;
6227 public Integer otherMethod(Integer param) { return param; }
6228 \@Deprecated
6229 public Integer removedMethod(Integer param1, String param2) { return param1; }
6230 }");
6231 writeFile($Path_v2."/RemovedDeprecatedMethod.java",
6232 "package $PackageName;
6233 public class RemovedDeprecatedMethod {
6234 public Integer field = 100;
6235 public Integer otherMethod(Integer param) { return param; }
6236 }");
6237
6238 # Interface_Removed_Abstract_Method
6239 writeFile($Path_v1."/InterfaceRemovedAbstractMethod.java",
6240 "package $PackageName;
6241 public interface InterfaceRemovedAbstractMethod extends BaseInterface, BaseInterface2 {
6242 public void removedMethod(Integer param1, java.io.ObjectOutput param2);
6243 public void someMethod(Integer param);
6244 }");
6245 writeFile($Path_v2."/InterfaceRemovedAbstractMethod.java",
6246 "package $PackageName;
6247 public interface InterfaceRemovedAbstractMethod extends BaseInterface, BaseInterface2 {
6248 public void someMethod(Integer param);
6249 }");
6250
6251 # Interface_Added_Abstract_Method
6252 writeFile($Path_v1."/InterfaceAddedAbstractMethod.java",
6253 "package $PackageName;
6254 public interface InterfaceAddedAbstractMethod extends BaseInterface, BaseInterface2 {
6255 public void someMethod(Integer param);
6256 }");
6257 writeFile($Path_v2."/InterfaceAddedAbstractMethod.java",
6258 "package $PackageName;
6259 public interface InterfaceAddedAbstractMethod extends BaseInterface, BaseInterface2 {
6260 public void someMethod(Integer param);
6261 public Integer addedMethod(Integer param);
6262 }");
6263
6264 # Interface_Added_Default_Method
6265 writeFile($Path_v1."/InterfaceAddedDefaultMethod.java",
6266 "package $PackageName;
6267 public interface InterfaceAddedDefaultMethod {
6268 public void someMethod(Integer param);
6269 }");
6270 writeFile($Path_v2."/InterfaceAddedDefaultMethod.java",
6271 "package $PackageName;
6272 public interface InterfaceAddedDefaultMethod {
6273 public void someMethod(Integer param);
6274 default Integer addedMethod(Integer param) { return 0; }
6275 }");
6276
6277 # Method_Became_NonDefault
6278 writeFile($Path_v1."/MethodBecameNonDefault.java",
6279 "package $PackageName;
6280 public interface MethodBecameNonDefault {
6281 default Integer someMethod(Integer param) { return 0; }
6282 }");
6283 writeFile($Path_v2."/MethodBecameNonDefault.java",
6284 "package $PackageName;
6285 public interface MethodBecameNonDefault {
6286 public Integer someMethod(Integer param);
6287 }");
6288
6289 writeFile($TestsPath."/Test_MethodBecameNonDefault.java",
6290 "import $PackageName.*;
6291 class Class_MethodBecameNonDefault implements MethodBecameNonDefault {
6292 };
6293
6294 public class Test_MethodBecameNonDefault
6295 {
6296 public static void main(String[] args)
6297 {
6298 Class_MethodBecameNonDefault Obj = new Class_MethodBecameNonDefault();
6299 Integer Res = Obj.someMethod(0);
6300 }
6301 }");
6302
6303 # Variable_Arity_To_Array
6304 writeFile($Path_v1."/VariableArityToArray.java",
6305 "package $PackageName;
6306 public class VariableArityToArray {
6307 public void someMethod(Integer x, String... y) { };
6308 }");
6309 writeFile($Path_v2."/VariableArityToArray.java",
6310 "package $PackageName;
6311 public class VariableArityToArray {
6312 public void someMethod(Integer x, String[] y) { };
6313 }");
6314
6315 # Class_Became_Interface
6316 writeFile($Path_v1."/ClassBecameInterface.java",
6317 "package $PackageName;
6318 public class ClassBecameInterface extends BaseClass {
6319 public Integer someMethod(Integer param) {
6320 return param;
6321 }
6322 }");
6323 writeFile($Path_v2."/ClassBecameInterface.java",
6324 "package $PackageName;
6325 public interface ClassBecameInterface extends BaseInterface, BaseInterface2 {
6326 public Integer someMethod(Integer param);
6327 }");
6328
6329 # Added_Super_Class
6330 writeFile($Path_v1."/AddedSuperClass.java",
6331 "package $PackageName;
6332 public class AddedSuperClass {
6333 public Integer someMethod(Integer param) {
6334 return param;
6335 }
6336 }");
6337 writeFile($Path_v2."/AddedSuperClass.java",
6338 "package $PackageName;
6339 public class AddedSuperClass extends BaseClass {
6340 public Integer someMethod(Integer param) {
6341 return param;
6342 }
6343 }");
6344
6345 # Abstract_Class_Added_Super_Abstract_Class
6346 writeFile($Path_v1."/AbstractClassAddedSuperAbstractClass.java",
6347 "package $PackageName;
6348 public abstract class AbstractClassAddedSuperAbstractClass {
6349 public Integer someMethod(Integer param) {
6350 return param;
6351 }
6352 }");
6353 writeFile($Path_v2."/AbstractClassAddedSuperAbstractClass.java",
6354 "package $PackageName;
6355 public abstract class AbstractClassAddedSuperAbstractClass extends BaseAbstractClass {
6356 public Integer someMethod(Integer param) {
6357 return param;
6358 }
6359 }");
6360
6361 # Removed_Super_Class
6362 writeFile($Path_v1."/RemovedSuperClass.java",
6363 "package $PackageName;
6364 public class RemovedSuperClass extends BaseClass {
6365 public Integer someMethod(Integer param) {
6366 return param;
6367 }
6368 }");
6369 writeFile($Path_v2."/RemovedSuperClass.java",
6370 "package $PackageName;
6371 public class RemovedSuperClass {
6372 public Integer someMethod(Integer param) {
6373 return param;
6374 }
6375 }");
6376
6377 # Changed_Super_Class
6378 writeFile($Path_v1."/ChangedSuperClass.java",
6379 "package $PackageName;
6380 public class ChangedSuperClass extends BaseClass {
6381 public Integer someMethod(Integer param) {
6382 return param;
6383 }
6384 }");
6385 writeFile($Path_v2."/ChangedSuperClass.java",
6386 "package $PackageName;
6387 public class ChangedSuperClass extends BaseClass2 {
6388 public Integer someMethod(Integer param) {
6389 return param;
6390 }
6391 }");
6392
6393 # Abstract_Class_Added_Super_Interface
6394 writeFile($Path_v1."/AbstractClassAddedSuperInterface.java",
6395 "package $PackageName;
6396 public abstract class AbstractClassAddedSuperInterface implements BaseInterface {
6397 public Integer method(Integer param) {
6398 return param;
6399 }
6400 }");
6401 writeFile($Path_v2."/AbstractClassAddedSuperInterface.java",
6402 "package $PackageName;
6403 public abstract class AbstractClassAddedSuperInterface implements BaseInterface, BaseInterface2 {
6404 public Integer method(Integer param) {
6405 return param;
6406 }
6407 }");
6408
6409 # Abstract_Class_Added_Super_Interface_With_Implemented_Methods
6410 writeFile($Path_v1."/AbstractClassAddedSuperInterfaceWithImplementedMethods.java",
6411 "package $PackageName;
6412 public abstract class AbstractClassAddedSuperInterfaceWithImplementedMethods implements BaseInterface {
6413 public Integer method(Integer param) {
6414 return param;
6415 }
6416 public Integer method2(Integer param) {
6417 return param;
6418 }
6419 }");
6420 writeFile($Path_v2."/AbstractClassAddedSuperInterfaceWithImplementedMethods.java",
6421 "package $PackageName;
6422 public abstract class AbstractClassAddedSuperInterfaceWithImplementedMethods implements BaseInterface, BaseInterface2 {
6423 public Integer method(Integer param) {
6424 return param;
6425 }
6426 public Integer method2(Integer param) {
6427 return param;
6428 }
6429 }");
6430
6431 # Class_Removed_Super_Interface
6432 writeFile($Path_v1."/ClassRemovedSuperInterface.java",
6433 "package $PackageName;
6434 public class ClassRemovedSuperInterface implements BaseInterface, BaseInterface2 {
6435 public Integer method(Integer param) {
6436 return param;
6437 }
6438 public Integer method2(Integer param) {
6439 return param;
6440 }
6441 }");
6442 writeFile($Path_v2."/ClassRemovedSuperInterface.java",
6443 "package $PackageName;
6444 public class ClassRemovedSuperInterface implements BaseInterface {
6445 public Integer method(Integer param) {
6446 return param;
6447 }
6448 public Integer method2(Integer param) {
6449 return param;
6450 }
6451 }");
6452
6453 writeFile($TestsPath."/Test_ClassRemovedSuperInterface.java",
6454 "import $PackageName.*;
6455 public class Test_ClassRemovedSuperInterface
6456 {
6457 public static void main(String[] args)
6458 {
6459 ClassRemovedSuperInterface Obj = new ClassRemovedSuperInterface();
6460 Integer Res = Obj.method2(0);
6461 }
6462 }");
6463
6464 # Interface_Added_Super_Interface
6465 writeFile($Path_v1."/InterfaceAddedSuperInterface.java",
6466 "package $PackageName;
6467 public interface InterfaceAddedSuperInterface extends BaseInterface {
6468 public Integer someMethod(Integer param);
6469 }");
6470 writeFile($Path_v2."/InterfaceAddedSuperInterface.java",
6471 "package $PackageName;
6472 public interface InterfaceAddedSuperInterface extends BaseInterface, BaseInterface2 {
6473 public Integer someMethod(Integer param);
6474 }");
6475
6476 # Interface_Added_Super_Constant_Interface
6477 writeFile($Path_v1."/InterfaceAddedSuperConstantInterface.java",
6478 "package $PackageName;
6479 public interface InterfaceAddedSuperConstantInterface extends BaseInterface {
6480 public Integer someMethod(Integer param);
6481 }");
6482 writeFile($Path_v2."/InterfaceAddedSuperConstantInterface.java",
6483 "package $PackageName;
6484 public interface InterfaceAddedSuperConstantInterface extends BaseInterface, BaseConstantInterface {
6485 public Integer someMethod(Integer param);
6486 }");
6487
6488 # Interface_Removed_Super_Interface
6489 writeFile($Path_v1."/InterfaceRemovedSuperInterface.java",
6490 "package $PackageName;
6491 public interface InterfaceRemovedSuperInterface extends BaseInterface, BaseInterface2 {
6492 public Integer someMethod(Integer param);
6493 }");
6494 writeFile($Path_v2."/InterfaceRemovedSuperInterface.java",
6495 "package $PackageName;
6496 public interface InterfaceRemovedSuperInterface extends BaseInterface {
6497 public Integer someMethod(Integer param);
6498 }");
6499
6500 # Interface_Removed_Super_Constant_Interface
6501 writeFile($Path_v1."/InterfaceRemovedSuperConstantInterface.java",
6502 "package $PackageName;
6503 public interface InterfaceRemovedSuperConstantInterface extends BaseInterface, BaseConstantInterface {
6504 public Integer someMethod(Integer param);
6505 }");
6506 writeFile($Path_v2."/InterfaceRemovedSuperConstantInterface.java",
6507 "package $PackageName;
6508 public interface InterfaceRemovedSuperConstantInterface extends BaseInterface {
6509 public Integer someMethod(Integer param);
6510 }");
6511
6512 # Interface_Became_Class
6513 writeFile($Path_v1."/InterfaceBecameClass.java",
6514 "package $PackageName;
6515 public interface InterfaceBecameClass extends BaseInterface, BaseInterface2 {
6516 public Integer someMethod(Integer param);
6517 }");
6518 writeFile($Path_v2."/InterfaceBecameClass.java",
6519 "package $PackageName;
6520 public class InterfaceBecameClass extends BaseClass {
6521 public Integer someMethod(Integer param) {
6522 return param;
6523 }
6524 }");
6525
6526 # Removed_Class
6527 writeFile($Path_v1."/RemovedClass.java",
6528 "package $PackageName;
6529 public class RemovedClass extends BaseClass {
6530 public Integer someMethod(Integer param){
6531 return param;
6532 }
6533 }");
6534
6535 # Removed_Class (w/o methods)
6536 writeFile($Path_v1."/RemovedFieldClass.java",
6537 "package $PackageName;
6538 public class RemovedFieldClass {
6539 public Integer field;
6540 }");
6541
6542 writeFile($TestsPath."/Test_RemovedFieldClass.java",
6543 "import $PackageName.*;
6544 public class Test_RemovedFieldClass extends RemovedFieldClass
6545 {
6546 public static void main(String[] args)
6547 {
6548 RemovedFieldClass X = new RemovedFieldClass();
6549 Integer Copy = X.field;
6550 }
6551 }");
6552
6553 # Removed_Class (with static fields, private constructor)
6554 writeFile($Path_v1."/RemovedClassWithStaticField.java",
6555 "package $PackageName;
6556 public class RemovedClassWithStaticField
6557 {
6558 private RemovedClassWithStaticField(){}
6559 public static Integer cnt = 0;
6560 }");
6561
6562 writeFile($TestsPath."/Test_RemovedClassWithStaticField.java",
6563 "import $PackageName.*;
6564 public class Test_RemovedClassWithStaticField
6565 {
6566 public static void main(String[] args)
6567 {
6568 Integer Copy = RemovedClassWithStaticField.cnt;
6569 }
6570 }");
6571
6572 # Removed_Field (static field, private constructor)
6573 writeFile($Path_v1."/RemovedStaticFieldFromClassWithPrivateCtor.java",
6574 "package $PackageName;
6575 public class RemovedStaticFieldFromClassWithPrivateCtor
6576 {
6577 private RemovedStaticFieldFromClassWithPrivateCtor(){}
6578 public static Integer cnt = 0;
6579 }");
6580
6581 writeFile($Path_v2."/RemovedStaticFieldFromClassWithPrivateCtor.java",
6582 "package $PackageName;
6583 public class RemovedStaticFieldFromClassWithPrivateCtor
6584 {
6585 private RemovedStaticFieldFromClassWithPrivateCtor(){}
6586 }");
6587
6588 writeFile($TestsPath."/Test_RemovedStaticFieldFromClassWithPrivateCtor.java",
6589 "import $PackageName.*;
6590 public class Test_RemovedStaticFieldFromClassWithPrivateCtor
6591 {
6592 public static void main(String[] args)
6593 {
6594 Integer Copy = RemovedStaticFieldFromClassWithPrivateCtor.cnt;
6595 }
6596 }");
6597
6598 # Removed_Constant_Field
6599 writeFile($Path_v1."/ClassRemovedStaticConstantField.java",
6600 "package $PackageName;
6601 public class ClassRemovedStaticConstantField
6602 {
6603 public static int removedField_Int = 1000;
6604 public static String removedField_Str = \"Value\";
6605 }");
6606 writeFile($Path_v2."/ClassRemovedStaticConstantField.java",
6607 "package $PackageName;
6608 public class ClassRemovedStaticConstantField {
6609 }");
6610
6611 writeFile($TestsPath."/Test_ClassRemovedStaticConstantField.java",
6612 "import $PackageName.*;
6613 public class Test_ClassRemovedStaticConstantField
6614 {
6615 public static void main(String[] args)
6616 {
6617 Integer Copy = ClassRemovedStaticConstantField.removedField_Int;
6618 }
6619 }");
6620
6621 # Removed_Class (Deprecated)
6622 writeFile($Path_v1."/RemovedDeprecatedClass.java",
6623 "package $PackageName;
6624 \@Deprecated
6625 public class RemovedDeprecatedClass {
6626 public Integer someMethod(Integer param){
6627 return param;
6628 }
6629 }");
6630
6631 # Removed_Interface
6632 writeFile($Path_v1."/RemovedInterface.java",
6633 "package $PackageName;
6634 public interface RemovedInterface extends BaseInterface, BaseInterface2 {
6635 public Integer someMethod(Integer param);
6636 }");
6637
6638 # NonAbstract_Class_Added_Abstract_Method
6639 writeFile($Path_v1."/NonAbstractClassAddedAbstractMethod.java",
6640 "package $PackageName;
6641 public class NonAbstractClassAddedAbstractMethod {
6642 public Integer someMethod(Integer param1, String[] param2) {
6643 return param1;
6644 };
6645 }");
6646 writeFile($Path_v2."/NonAbstractClassAddedAbstractMethod.java",
6647 "package $PackageName;
6648 public abstract class NonAbstractClassAddedAbstractMethod {
6649 public Integer someMethod(Integer param1, String[] param2) {
6650 return param1;
6651 };
6652 public abstract Integer addedMethod(Integer param);
6653 }");
6654
6655 # Abstract_Class_Added_Abstract_Method
6656 writeFile($Path_v1."/AbstractClassAddedAbstractMethod.java",
6657 "package $PackageName;
6658 public abstract class AbstractClassAddedAbstractMethod {
6659 public Integer someMethod(Integer param1, String[] param2) {
6660 return param1;
6661 };
6662 }");
6663 writeFile($Path_v2."/AbstractClassAddedAbstractMethod.java",
6664 "package $PackageName;
6665 public abstract class AbstractClassAddedAbstractMethod {
6666 public Integer someMethod(Integer param1, String[] param2) {
6667 return param1;
6668 };
6669 public abstract Integer addedMethod(Integer param);
6670 }");
6671
6672 # Class_Became_Abstract
6673 writeFile($Path_v1."/ClassBecameAbstract.java",
6674 "package $PackageName;
6675 public class ClassBecameAbstract {
6676 public Integer someMethod(Integer param1, String[] param2) {
6677 return param1;
6678 };
6679 }");
6680 writeFile($Path_v2."/ClassBecameAbstract.java",
6681 "package $PackageName;
6682 public abstract class ClassBecameAbstract {
6683 public Integer someMethod(Integer param1, String[] param2) {
6684 return param1;
6685 };
6686 public abstract Integer addedMethod(Integer param);
6687 }");
6688
6689 # Class_Became_Final
6690 writeFile($Path_v1."/ClassBecameFinal.java",
6691 "package $PackageName;
6692 public class ClassBecameFinal {
6693 public Integer someMethod(Integer param1, String[] param2) {
6694 return param1;
6695 };
6696 }");
6697 writeFile($Path_v2."/ClassBecameFinal.java",
6698 "package $PackageName;
6699 public final class ClassBecameFinal {
6700 public Integer someMethod(Integer param1, String[] param2) {
6701 return param1;
6702 };
6703 }");
6704
6705 # Class_Removed_Abstract_Method
6706 writeFile($Path_v1."/ClassRemovedAbstractMethod.java",
6707 "package $PackageName;
6708 public abstract class ClassRemovedAbstractMethod {
6709 public Integer someMethod(Integer param1, String[] param2) {
6710 return param1;
6711 };
6712 public abstract Integer removedMethod(Integer param);
6713 }");
6714 writeFile($Path_v2."/ClassRemovedAbstractMethod.java",
6715 "package $PackageName;
6716 public abstract class ClassRemovedAbstractMethod {
6717 public Integer someMethod(Integer param1, String[] param2) {
6718 return param1;
6719 };
6720 }");
6721
6722 # Class_Method_Became_Abstract
6723 writeFile($Path_v1."/ClassMethodBecameAbstract.java",
6724 "package $PackageName;
6725 public abstract class ClassMethodBecameAbstract {
6726 public Integer someMethod(Integer param1, String[] param2) {
6727 return param1;
6728 };
6729 public Integer someMethod(Integer param){
6730 return param;
6731 };
6732 }");
6733 writeFile($Path_v2."/ClassMethodBecameAbstract.java",
6734 "package $PackageName;
6735 public abstract class ClassMethodBecameAbstract {
6736 public Integer someMethod(Integer param1, String[] param2) {
6737 return param1;
6738 };
6739 public abstract Integer someMethod(Integer param);
6740 }");
6741
6742 # Class_Method_Became_NonAbstract
6743 writeFile($Path_v1."/ClassMethodBecameNonAbstract.java",
6744 "package $PackageName;
6745 public abstract class ClassMethodBecameNonAbstract {
6746 public Integer someMethod(Integer param1, String[] param2) {
6747 return param1;
6748 };
6749 public abstract Integer someMethod(Integer param);
6750 }");
6751 writeFile($Path_v2."/ClassMethodBecameNonAbstract.java",
6752 "package $PackageName;
6753 public abstract class ClassMethodBecameNonAbstract {
6754 public Integer someMethod(Integer param1, String[] param2) {
6755 return param1;
6756 };
6757 public Integer someMethod(Integer param){
6758 return param;
6759 };
6760 }");
6761
6762 # Method_Became_Static
6763 writeFile($Path_v1."/MethodBecameStatic.java",
6764 "package $PackageName;
6765 public class MethodBecameStatic {
6766 public Integer someMethod(Integer param) {
6767 return param;
6768 };
6769 }");
6770 writeFile($Path_v2."/MethodBecameStatic.java",
6771 "package $PackageName;
6772 public class MethodBecameStatic {
6773 public static Integer someMethod(Integer param) {
6774 return param;
6775 };
6776 }");
6777
6778 # Method_Became_NonStatic
6779 writeFile($Path_v1."/MethodBecameNonStatic.java",
6780 "package $PackageName;
6781 public class MethodBecameNonStatic {
6782 public static Integer someMethod(Integer param) {
6783 return param;
6784 };
6785 }");
6786 writeFile($Path_v2."/MethodBecameNonStatic.java",
6787 "package $PackageName;
6788 public class MethodBecameNonStatic {
6789 public Integer someMethod(Integer param) {
6790 return param;
6791 };
6792 }");
6793
6794 # Static_Method_Became_Final
6795 writeFile($Path_v1."/StaticMethodBecameFinal.java",
6796 "package $PackageName;
6797 public class StaticMethodBecameFinal {
6798 public static Integer someMethod(Integer param) {
6799 return param;
6800 };
6801 }");
6802 writeFile($Path_v2."/StaticMethodBecameFinal.java",
6803 "package $PackageName;
6804 public class StaticMethodBecameFinal {
6805 public static final Integer someMethod(Integer param) {
6806 return param;
6807 };
6808 }");
6809
6810 # NonStatic_Method_Became_Final
6811 writeFile($Path_v1."/NonStaticMethodBecameFinal.java",
6812 "package $PackageName;
6813 public class NonStaticMethodBecameFinal {
6814 public Integer someMethod(Integer param) {
6815 return param;
6816 };
6817 }");
6818 writeFile($Path_v2."/NonStaticMethodBecameFinal.java",
6819 "package $PackageName;
6820 public class NonStaticMethodBecameFinal {
6821 public final Integer someMethod(Integer param) {
6822 return param;
6823 };
6824 }");
6825
6826 # Method_Became_Abstract
6827 writeFile($Path_v1."/MethodBecameAbstract.java",
6828 "package $PackageName;
6829 public abstract class MethodBecameAbstract {
6830 public Integer someMethod(Integer param) {
6831 return param;
6832 };
6833 }");
6834 writeFile($Path_v2."/MethodBecameAbstract.java",
6835 "package $PackageName;
6836 public abstract class MethodBecameAbstract {
6837 public abstract Integer someMethod(Integer param);
6838 }");
6839
6840 # Method_Became_NonAbstract
6841 writeFile($Path_v1."/MethodBecameNonAbstract.java",
6842 "package $PackageName;
6843 public abstract class MethodBecameNonAbstract {
6844 public abstract Integer someMethod(Integer param);
6845 }");
6846 writeFile($Path_v2."/MethodBecameNonAbstract.java",
6847 "package $PackageName;
6848 public abstract class MethodBecameNonAbstract {
6849 public Integer someMethod(Integer param) {
6850 return param;
6851 };
6852 }");
6853
6854 # Changed_Method_Access
6855 writeFile($Path_v1."/ChangedMethodAccess.java",
6856 "package $PackageName;
6857 public class ChangedMethodAccess {
6858 public Integer someMethod(Integer param) {
6859 return param;
6860 };
6861 }");
6862 writeFile($Path_v2."/ChangedMethodAccess.java",
6863 "package $PackageName;
6864 public class ChangedMethodAccess {
6865 protected Integer someMethod(Integer param) {
6866 return param;
6867 };
6868 }");
6869
6870 # Method_Became_Synchronized
6871 writeFile($Path_v1."/MethodBecameSynchronized.java",
6872 "package $PackageName;
6873 public class MethodBecameSynchronized {
6874 public Integer someMethod(Integer param) {
6875 return param;
6876 };
6877 }");
6878 writeFile($Path_v2."/MethodBecameSynchronized.java",
6879 "package $PackageName;
6880 public class MethodBecameSynchronized {
6881 public synchronized Integer someMethod(Integer param) {
6882 return param;
6883 };
6884 }");
6885
6886 # Method_Became_NonSynchronized
6887 writeFile($Path_v1."/MethodBecameNonSynchronized.java",
6888 "package $PackageName;
6889 public class MethodBecameNonSynchronized {
6890 public synchronized Integer someMethod(Integer param) {
6891 return param;
6892 };
6893 }");
6894 writeFile($Path_v2."/MethodBecameNonSynchronized.java",
6895 "package $PackageName;
6896 public class MethodBecameNonSynchronized {
6897 public Integer someMethod(Integer param) {
6898 return param;
6899 };
6900 }");
6901
6902 # Class_Overridden_Method
6903 writeFile($Path_v1."/OverriddenMethod.java",
6904 "package $PackageName;
6905 public class OverriddenMethod extends BaseClass {
6906 public Integer someMethod(Integer param) { return param; }
6907 }");
6908 writeFile($Path_v2."/OverriddenMethod.java",
6909 "package $PackageName;
6910 public class OverriddenMethod extends BaseClass {
6911 public Integer someMethod(Integer param) { return param; }
6912 public Integer method(Integer param) { return 2*param; }
6913 }");
6914
6915 # Class_Method_Moved_Up_Hierarchy
6916 writeFile($Path_v1."/ClassMethodMovedUpHierarchy.java",
6917 "package $PackageName;
6918 public class ClassMethodMovedUpHierarchy extends BaseClass {
6919 public Integer someMethod(Integer param) { return param; }
6920 public Integer method(Integer param) { return 2*param; }
6921 }");
6922 writeFile($Path_v2."/ClassMethodMovedUpHierarchy.java",
6923 "package $PackageName;
6924 public class ClassMethodMovedUpHierarchy extends BaseClass {
6925 public Integer someMethod(Integer param) { return param; }
6926 }");
6927
6928 # Class_Method_Moved_Up_Hierarchy (Interface Method) - should not be reported
6929 writeFile($Path_v1."/InterfaceMethodMovedUpHierarchy.java",
6930 "package $PackageName;
6931 public interface InterfaceMethodMovedUpHierarchy extends BaseInterface {
6932 public Integer method(Integer param);
6933 public Integer method2(Integer param);
6934 }");
6935 writeFile($Path_v2."/InterfaceMethodMovedUpHierarchy.java",
6936 "package $PackageName;
6937 public interface InterfaceMethodMovedUpHierarchy extends BaseInterface {
6938 public Integer method2(Integer param);
6939 }");
6940
6941 # Class_Method_Moved_Up_Hierarchy (Abstract Method) - should not be reported
6942 writeFile($Path_v1."/AbstractMethodMovedUpHierarchy.java",
6943 "package $PackageName;
6944 public abstract class AbstractMethodMovedUpHierarchy implements BaseInterface {
6945 public abstract Integer method(Integer param);
6946 public abstract Integer method2(Integer param);
6947 }");
6948 writeFile($Path_v2."/AbstractMethodMovedUpHierarchy.java",
6949 "package $PackageName;
6950 public abstract class AbstractMethodMovedUpHierarchy implements BaseInterface {
6951 public abstract Integer method2(Integer param);
6952 }");
6953
6954 # Use
6955 writeFile($Path_v1."/Use.java",
6956 "package $PackageName;
6957 public class Use
6958 {
6959 public FieldBecameFinal field;
6960 public void someMethod(FieldBecameFinal[] param) { };
6961 public void someMethod(Use param) { };
6962 public Integer someMethod(AbstractClassAddedSuperAbstractClass param) {
6963 return 0;
6964 }
6965 public Integer someMethod(AbstractClassAddedAbstractMethod param) {
6966 return 0;
6967 }
6968 public Integer someMethod(InterfaceAddedAbstractMethod param) {
6969 return 0;
6970 }
6971 public Integer someMethod(InterfaceAddedSuperInterface param) {
6972 return 0;
6973 }
6974 public Integer someMethod(AbstractClassAddedSuperInterface param) {
6975 return 0;
6976 }
6977 public Integer someMethod(AbstractClassAddedSuperInterfaceWithImplementedMethods param) {
6978 return 0;
6979 }
6980 }");
6981 writeFile($Path_v2."/Use.java",
6982 "package $PackageName;
6983 public class Use
6984 {
6985 public FieldBecameFinal field;
6986 public void someMethod(FieldBecameFinal[] param) { };
6987 public void someMethod(Use param) { };
6988 public Integer someMethod(AbstractClassAddedSuperAbstractClass param) {
6989 return param.abstractMethod(100)+param.field;
6990 }
6991 public Integer someMethod(AbstractClassAddedAbstractMethod param) {
6992 return param.addedMethod(100);
6993 }
6994 public Integer someMethod(InterfaceAddedAbstractMethod param) {
6995 return param.addedMethod(100);
6996 }
6997 public Integer someMethod(InterfaceAddedSuperInterface param) {
6998 return param.method2(100);
6999 }
7000 public Integer someMethod(AbstractClassAddedSuperInterface param) {
7001 return param.method2(100);
7002 }
7003 public Integer someMethod(AbstractClassAddedSuperInterfaceWithImplementedMethods param) {
7004 return param.method2(100);
7005 }
7006 }");
7007
7008 # Added_Package
7009 writeFile($Path_v2."/AddedPackage/AddedPackageClass.java",
7010 "package $PackageName.AddedPackage;
7011 public class AddedPackageClass {
7012 public Integer field;
7013 public void someMethod(Integer param) { };
7014 }");
7015
7016 # Removed_Package
7017 writeFile($Path_v1."/RemovedPackage/RemovedPackageClass.java",
7018 "package $PackageName.RemovedPackage;
7019 public class RemovedPackageClass {
7020 public Integer field;
7021 public void someMethod(Integer param) { };
7022 }");
7023 my $BuildRoot1 = get_dirname($Path_v1);
7024 my $BuildRoot2 = get_dirname($Path_v2);
7025 if(compileJavaLib($LibName, $BuildRoot1, $BuildRoot2))
7026 {
7027 runTests($TestsPath, $PackageName, $BuildRoot1, $BuildRoot2);
7028 runChecker($LibName, $BuildRoot1, $BuildRoot2);
7029 }
7030}
7031
7032sub readArchive($$)
7033{ # 1, 2 - library, 0 - client
7034 my ($LibVersion, $Path) = @_;
7035 return if(not $Path or not -e $Path);
7036
7037 if($LibVersion)
7038 {
7039 my $ArchiveName = get_filename($Path);
7040 $LibArchives{$LibVersion}{$ArchiveName} = 1;
7041 }
7042
7043 $Path = get_abs_path($Path);
7044 my $JarCmd = get_CmdPath("jar");
7045 if(not $JarCmd) {
7046 exitStatus("Not_Found", "can't find \"jar\" command");
7047 }
7048 my $ExtractPath = joinPath($TMP_DIR, $ExtractCounter);
7049 if(-d $ExtractPath) {
7050 rmtree($ExtractPath);
7051 }
7052 mkpath($ExtractPath);
7053 chdir($ExtractPath);
7054 system($JarCmd." -xf \"$Path\"");
7055 if($?) {
7056 exitStatus("Error", "can't extract \'$Path\'");
7057 }
7058 chdir($ORIG_DIR);
7059 my @Classes = ();
7060 foreach my $ClassPath (cmd_find($ExtractPath,"","*\.class",""))
7061 {
7062 if($OSgroup ne "windows") {
7063 $ClassPath=~s/\.class\Z//g;
7064 }
7065
7066 my $ClassName = get_filename($ClassPath);
7067 if($ClassName=~/\$\d/) {
7068 next;
7069 }
7070 $ClassPath = cut_path_prefix($ClassPath, $ExtractPath); # javap decompiler accepts relative paths only
7071
7072 my $ClassDir = get_dirname($ClassPath);
7073 if($ClassDir=~/\./)
7074 { # jaxb-osgi.jar/1.0/org/apache
7075 next;
7076 }
7077
7078 my $Package = get_PFormat($ClassDir);
7079 if($LibVersion)
7080 {
7081 if(skipPackage($Package, $LibVersion))
7082 { # internal packages
7083 next;
7084 }
7085 }
7086
7087 $ClassName=~s/\$/./g; # real name for GlyphView$GlyphPainter is GlyphView.GlyphPainter
7088 push(@Classes, $ClassPath);
7089 }
7090
7091 if($#Classes!=-1)
7092 {
7093 foreach my $PartRef (divideArray(\@Classes))
7094 {
7095 if($LibVersion) {
7096 readClasses($PartRef, $LibVersion, get_filename($Path));
7097 }
7098 else {
7099 readClasses_Usage($PartRef);
7100 }
7101 }
7102 }
7103
7104 $ExtractCounter+=1;
7105
7106 if($LibVersion)
7107 {
7108 foreach my $SubArchive (cmd_find($ExtractPath,"","*\.jar",""))
7109 { # recursive step
7110 readArchive($LibVersion, $SubArchive);
7111 }
7112 }
7113
7114 rmtree($ExtractPath);
7115}
7116
7117sub native_path($)
7118{
7119 my $Path = $_[0];
7120 if($OSgroup eq "windows") {
7121 $Path=~s/[\/\\]+/\\/g;
7122 }
7123 return $Path;
7124}
7125
7126sub divideArray($)
7127{
7128 my $ArrRef = $_[0];
7129 return () if(not $ArrRef);
7130 my @Array = @{$ArrRef};
7131 return () if($#{$ArrRef}==-1);
7132
7133 my @Res = ();
7134 my $Sub = [];
7135 my $Len = 0;
7136
7137 foreach my $Pos (0 .. $#{$ArrRef})
7138 {
7139 my $Arg = $ArrRef->[$Pos];
7140 my $Arg_L = length($Arg) + 1; # space
7141 if($Len < $ARG_MAX - 250)
7142 {
7143 push(@{$Sub}, $Arg);
7144 $Len += $Arg_L;
7145 }
7146 else
7147 {
7148 push(@Res, $Sub);
7149
7150 $Sub = [$Arg];
7151 $Len = $Arg_L;
7152 }
7153 }
7154
7155 if($#{$Sub}!=-1) {
7156 push(@Res, $Sub);
7157 }
7158
7159 return @Res;
7160}
7161
7162sub registerType($$)
7163{
7164 my ($TName, $LibVersion) = @_;
7165
7166 if(not $TName) {
7167 return 0;
7168 }
7169
7170 $TName=~s/#/./g;
7171 if($TName_Tid{$LibVersion}{$TName}) {
7172 return $TName_Tid{$LibVersion}{$TName};
7173 }
7174
7175 if(not $TName_Tid{$LibVersion}{$TName})
7176 {
7177 my $ID = undef;
7178 if($REPRODUCIBLE) {
7179 $ID = getMd5($TName);
7180 }
7181 else {
7182 $ID = ++$TYPE_ID;
7183 }
7184 $TName_Tid{$LibVersion}{$TName} = "$ID";
7185 }
7186
7187 my $Tid = $TName_Tid{$LibVersion}{$TName};
7188 $TypeInfo{$LibVersion}{$Tid}{"Name"} = $TName;
7189 if($TName=~/(.+)\[\]\Z/)
7190 {
7191 if(my $BaseTypeId = registerType($1, $LibVersion))
7192 {
7193 $TypeInfo{$LibVersion}{$Tid}{"BaseType"} = $BaseTypeId;
7194 $TypeInfo{$LibVersion}{$Tid}{"Type"} = "array";
7195 }
7196 }
7197
7198 return $Tid;
7199}
7200
7201sub readClasses_Usage($)
7202{
7203 my $Paths = $_[0];
7204 return () if(not $Paths);
7205
7206 my $JavapCmd = get_CmdPath("javap");
7207 if(not $JavapCmd) {
7208 exitStatus("Not_Found", "can't find \"javap\" command");
7209 }
7210
7211 my $Input = join(" ", @{$Paths});
7212 if($OSgroup ne "windows")
7213 { # on unix ensure that the system does not try and interpret the $, by escaping it
7214 $Input=~s/\$/\\\$/g;
7215 }
7216
7217 chdir($TMP_DIR."/".$ExtractCounter);
7218 open(CONTENT, "$JavapCmd -c -private $Input 2>\"$TMP_DIR/warn\" |");
7219 while(<CONTENT>)
7220 {
7221 if(/\/\/\s*(Method|InterfaceMethod)\s+(.+)\Z/)
7222 {
7223 my $M = $2;
7224 $UsedMethods_Client{$M} = 1;
7225
7226 if($M=~/\A(.*)+\.\w+\:\(/)
7227 {
7228 my $C = $1;
7229 $C=~s/\//./g;
7230 $UsedClasses_Client{$C} = 1;
7231 }
7232 }
7233 elsif(/\/\/\s*Field\s+(.+)\Z/)
7234 {
7235 my $FieldName = $1;
7236 if(/\s+(putfield|getfield|getstatic|putstatic)\s+/) {
7237 $UsedFields_Client{$FieldName} = $1;
7238 }
7239 }
7240 elsif(/ ([^\s]+) [^: ]+\(([^()]+)\)/)
7241 {
7242 my ($Ret, $Params) = ($1, $2);
7243
7244 $Ret=~s/\[\]//g; # quals
7245 $UsedClasses_Client{$Ret} = 1;
7246
7247 foreach my $Param (split(/\s*,\s*/, $Params))
7248 {
7249 $Param=~s/\[\]//g; # quals
7250 $UsedClasses_Client{$Param} = 1;
7251 }
7252 }
7253 elsif(/ class /)
7254 {
7255 if(/extends ([^\s{]+)/)
7256 {
7257 foreach my $Class (split(/\s*,\s*/, $1)) {
7258 $UsedClasses_Client{$Class} = 1;
7259 }
7260 }
7261
7262 if(/implements ([^\s{]+)/)
7263 {
7264 foreach my $Interface (split(/\s*,\s*/, $1)) {
7265 $UsedClasses_Client{$Interface} = 1;
7266 }
7267 }
7268 }
7269 }
7270 close(CONTENT);
7271 chdir($ORIG_DIR);
7272}
7273
7274sub readClasses($$$)
7275{
7276 my ($Paths, $LibVersion, $ArchiveName) = @_;
7277 return if(not $Paths or not $LibVersion or not $ArchiveName);
7278
7279 my $JavapCmd = get_CmdPath("javap");
7280 if(not $JavapCmd) {
7281 exitStatus("Not_Found", "can't find \"javap\" command");
7282 }
7283
7284 my $Input = join(" ", @{$Paths});
7285 if($OSgroup ne "windows")
7286 { # on unix ensure that the system does not try and interpret the $, by escaping it
7287 $Input=~s/\$/\\\$/g;
7288 }
7289
7290 my $Output = $TMP_DIR."/class-dump.txt";
7291 if(-e $Output) {
7292 unlink($Output);
7293 }
7294
7295 my $Cmd = "$JavapCmd -s -private";
7296 if(not $Quick) {
7297 $Cmd .= " -c -verbose";
7298 }
7299
7300 chdir($TMP_DIR."/".$ExtractCounter);
7301 system($Cmd." ".$Input." >\"$Output\" 2>\"$TMP_DIR/warn\"");
7302 chdir($ORIG_DIR);
7303
7304 if(not -e $Output) {
7305 exitStatus("Error", "internal error in parser, try to reduce ARG_MAX");
7306 }
7307 if($Debug) {
7308 appendFile($DEBUG_PATH{$LibVersion}."/class-dump.txt", readFile($Output));
7309 }
7310
7311 # ! private info should be processed
7312 open(CONTENT, "$TMP_DIR/class-dump.txt");
7313 my @Content = <CONTENT>;
7314 close(CONTENT);
7315
7316 my (%TypeAttr, $CurrentMethod, $CurrentPackage, $CurrentClass) = ();
7317 my ($InParamTable, $InExceptionTable, $InCode) = (0, 0, 0);
7318
7319 my $InAnnotations = undef;
7320 my $InAnnotations_Class = undef;
7321 my $InAnnotations_Method = undef;
7322 my %AnnotationName = ();
7323 my %AnnotationNum = (); # support for Java 7
7324
7325 my ($ParamPos, $FieldPos, $LineNum) = (0, 0, 0);
7326 while($LineNum<=$#Content)
7327 {
7328 my $LINE = $Content[$LineNum++];
7329 my $LINE_N = $Content[$LineNum];
7330
7331 if($LINE=~/\A\s*(?:const|AnnotationDefault|Compiled|Source|Constant)/) {
7332 next;
7333 }
7334
7335 if($LINE=~/\sof\s|\sline \d+:|\[\s*class|= \[|\$[\d\$\(:\.;]| class\$|[\.\/]\$|\._\d|\$eq/)
7336 { # artificial methods and code
7337 next;
7338 }
7339
7340 if($LINE=~/ (\w+\$|)\w+\$\w+[\(:]/) {
7341 next;
7342 }
7343
7344 # $LINE=~s/ \$(\w)/ $1/g;
7345 # $LINE_N=~s/ \$(\w)/ $1/g;
7346
7347 if(not $InParamTable)
7348 {
7349 if($LINE=~/ \$/) {
7350 next;
7351 }
7352 }
7353
7354 $LINE=~s/\$([\> ]|\Z)/$1/g;
7355 $LINE_N=~s/\$([\> ]|\Z)/$1/g;
7356
7357 if($LINE eq "\n" or $LINE eq "}\n")
7358 {
7359 $CurrentMethod = undef;
7360 $InCode = 0;
7361 $InAnnotations_Method = 0;
7362 $InParamTable = 0;
7363 }
7364
7365 if($LINE eq "}\n") {
7366 $InAnnotations_Class = 1;
7367 }
7368
7369 if($LINE=~/\A\s*#(\d+)/)
7370 { # Constant pool
7371 my $CNum = $1;
7372 if($LINE=~/\s+([^ ]+?);/)
7373 {
7374 my $AName = $1;
7375 $AName=~s/\AL//;
7376 $AName=~s/\$/./g;
7377 $AName=~s/\//./g;
7378
7379 $AnnotationName{$CNum} = $AName;
7380
7381 if(defined $AnnotationNum{$CNum})
7382 { # support for Java 7
7383 if($InAnnotations_Class) {
7384 $TypeAttr{"Annotations"}{registerType($AName, $LibVersion)} = 1;
7385 }
7386 delete($AnnotationNum{$CNum});
7387 }
7388 }
7389
7390 next;
7391 }
7392
7393 # Java 7: templates
7394 if(index($LINE, "<")!=-1)
7395 { # <T extends java.lang.Object>
7396 # <KEYIN extends java.lang.Object ...
7397 if($LINE=~/<[A-Z\d\?]+ /i)
7398 {
7399 while($LINE=~/<([A-Z\d\?]+ .*?)>( |\Z)/i)
7400 {
7401 my $Str = $1;
7402 my @Prms = ();
7403 foreach my $P (separate_Params($Str, 0, 0))
7404 {
7405 $P=~s/\A([A-Z\d\?]+) .*\Z/$1/ig;
7406 push(@Prms, $P);
7407 }
7408 my $Str_N = join(", ", @Prms);
7409 $LINE=~s/\Q$Str\E/$Str_N/g;
7410 }
7411 }
7412 }
7413
7414 $LINE=~s/\s*,\s*/,/g;
7415 $LINE=~s/\$/#/g;
7416
7417 if(index($LINE, "LocalVariableTable")!=-1) {
7418 $InParamTable += 1;
7419 }
7420 elsif($LINE=~/Exception\s+table/) {
7421 $InExceptionTable = 1;
7422 }
7423 elsif($LINE=~/\A\s*Code:/)
7424 {
7425 $InCode += 1;
7426 $InAnnotations = undef;
7427 }
7428 elsif($LINE=~/\A\s*\d+:\s*(.*)\Z/)
7429 { # read Code
7430 if($InCode==1)
7431 {
7432 if($CurrentMethod)
7433 {
7434 if(index($LINE, "invoke")!=-1)
7435 {
7436 if($LINE=~/ invoke(\w+) .* \/\/\s*(Method|InterfaceMethod)\s+(.+)\Z/)
7437 { # 3: invokevirtual #2; //Method "[Lcom/sleepycat/je/Database#DbState;".clone:()Ljava/lang/Object;
7438 my ($InvokeType, $InvokedName) = ($1, $3);
7439
7440 if($InvokedName!~/\A(\w+:|java\/(lang|util|io)\/)/
7441 and index($InvokedName, '"<init>":')!=0)
7442 {
7443 $InvokedName=~s/#/\$/g;
7444 $MethodUsed{$LibVersion}{$InvokedName}{$CurrentMethod} = $InvokeType;
7445 }
7446 }
7447 }
7448 # elsif($LINE=~/ (getstatic|putstatic) .* \/\/\s*Field\s+(.+)\Z/)
7449 # {
7450 # my $UsedFieldName = $2;
7451 # $FieldUsed{$LibVersion}{$UsedFieldName}{$CurrentMethod} = 1;
7452 # }
7453 }
7454 }
7455 elsif(defined $InAnnotations)
7456 {
7457 if($LINE=~/\A\s*\d+\:\s*#(\d+)/)
7458 {
7459 if(my $AName = $AnnotationName{$1})
7460 {
7461 if($InAnnotations_Class) {
7462 $TypeAttr{"Annotations"}{registerType($AName, $LibVersion)} = 1;
7463 }
7464 elsif($InAnnotations_Method) {
7465 $MethodInfo{$LibVersion}{$CurrentMethod}{"Annotations"}{registerType($AName, $LibVersion)} = 1;
7466 }
7467 }
7468 else
7469 { # suport for Java 7
7470 $AnnotationNum{$1} = 1;
7471 }
7472 }
7473 }
7474 }
7475 elsif($CurrentMethod and $InParamTable==1 and $LINE=~/\A\s+0\s+\d+\s+\d+\s+(\#?)(\w+)/)
7476 { # read parameter names from LocalVariableTable
7477 my $Art = $1;
7478 my $PName = $2;
7479
7480 if(($PName ne "this" or $Art) and $PName=~/[a-z]/i)
7481 {
7482 if($CurrentMethod)
7483 {
7484 if(defined $MethodInfo{$LibVersion}{$CurrentMethod}
7485 and defined $MethodInfo{$LibVersion}{$CurrentMethod}{"Param"}
7486 and defined $MethodInfo{$LibVersion}{$CurrentMethod}{"Param"}{$ParamPos}
7487 and defined $MethodInfo{$LibVersion}{$CurrentMethod}{"Param"}{$ParamPos}{"Type"})
7488 {
7489 $MethodInfo{$LibVersion}{$CurrentMethod}{"Param"}{$ParamPos}{"Name"} = $PName;
7490 $ParamPos++;
7491 }
7492 }
7493 }
7494 }
7495 elsif($CurrentClass and $LINE=~/(\A|\s+)([^\s]+)\s+([^\s]+)\s*\((.*)\)\s*(throws\s*([^\s]+)|)\s*;\Z/)
7496 { # attributes of methods and constructors
7497 my (%MethodAttr, $ParamsLine, $Exceptions) = ();
7498
7499 $InParamTable = 0; # read the first local variable table
7500 $InCode = 0; # read the first code
7501 $InAnnotations_Method = 1;
7502 $InAnnotations_Class = 0;
7503
7504 ($MethodAttr{"Return"}, $MethodAttr{"ShortName"}, $ParamsLine, $Exceptions) = ($2, $3, $4, $6);
7505 $MethodAttr{"ShortName"}=~s/#/./g;
7506
7507 if($Exceptions)
7508 {
7509 foreach my $E (split(/,/, $Exceptions)) {
7510 $MethodAttr{"Exceptions"}{registerType($E, $LibVersion)} = 1;
7511 }
7512 }
7513 if($LINE=~/(\A|\s+)(public|protected|private)\s+/) {
7514 $MethodAttr{"Access"} = $2;
7515 }
7516 else {
7517 $MethodAttr{"Access"} = "package-private";
7518 }
7519 $MethodAttr{"Class"} = registerType($TypeAttr{"Name"}, $LibVersion);
7520 if($MethodAttr{"ShortName"}=~/\A(|(.+)\.)\Q$CurrentClass\E\Z/)
7521 {
7522 if($2)
7523 {
7524 $MethodAttr{"Package"} = $2;
7525 $CurrentPackage = $MethodAttr{"Package"};
7526 $MethodAttr{"ShortName"} = $CurrentClass;
7527 }
7528 $MethodAttr{"Constructor"} = 1;
7529 delete($MethodAttr{"Return"});
7530 }
7531 else
7532 {
7533 $MethodAttr{"Return"} = registerType($MethodAttr{"Return"}, $LibVersion);
7534 }
7535
7536 my @Params = separate_Params($ParamsLine, 0, 1);
7537
7538 $ParamPos = 0;
7539 foreach my $ParamTName (@Params)
7540 {
7541 %{$MethodAttr{"Param"}{$ParamPos}} = ("Type"=>registerType($ParamTName, $LibVersion), "Name"=>"p".($ParamPos+1));
7542 $ParamPos++;
7543 }
7544 $ParamPos = 0;
7545 if(not $MethodAttr{"Constructor"})
7546 { # methods
7547 if($CurrentPackage) {
7548 $MethodAttr{"Package"} = $CurrentPackage;
7549 }
7550 if($LINE=~/(\A|\s+)abstract\s+/) {
7551 $MethodAttr{"Abstract"} = 1;
7552 }
7553 if($LINE=~/(\A|\s+)final\s+/) {
7554 $MethodAttr{"Final"} = 1;
7555 }
7556 if($LINE=~/(\A|\s+)static\s+/) {
7557 $MethodAttr{"Static"} = 1;
7558 }
7559 if($LINE=~/(\A|\s+)native\s+/) {
7560 $MethodAttr{"Native"} = 1;
7561 }
7562 if($LINE=~/(\A|\s+)synchronized\s+/) {
7563 $MethodAttr{"Synchronized"} = 1;
7564 }
7565 }
7566
7567 # read the Signature
7568 if($LINE_N=~/(Signature|descriptor):\s*(.+)\Z/i)
7569 { # create run-time unique name ( java/io/PrintStream.println (Ljava/lang/String;)V )
7570 if($MethodAttr{"Constructor"}) {
7571 $CurrentMethod = $CurrentClass.".\"<init>\":".$2;
7572 }
7573 else {
7574 $CurrentMethod = $CurrentClass.".".$MethodAttr{"ShortName"}.":".$2;
7575 }
7576 if(my $PackageName = get_SFormat($CurrentPackage)) {
7577 $CurrentMethod = $PackageName."/".$CurrentMethod;
7578 }
7579
7580 $LineNum++;
7581 }
7582 else {
7583 exitStatus("Error", "internal error - can't read method signature");
7584 }
7585
7586 $MethodAttr{"Archive"} = $ArchiveName;
7587 if($CurrentMethod)
7588 {
7589 %{$MethodInfo{$LibVersion}{$CurrentMethod}} = %MethodAttr;
7590 if($MethodAttr{"Access"}=~/public|protected/)
7591 {
7592 $Class_Methods{$LibVersion}{$TypeAttr{"Name"}}{$CurrentMethod} = 1;
7593 if($MethodAttr{"Abstract"}) {
7594 $Class_AbstractMethods{$LibVersion}{$TypeAttr{"Name"}}{$CurrentMethod} = 1;
7595 }
7596 }
7597 }
7598 }
7599 elsif($CurrentClass and $LINE=~/(\A|\s+)([^\s]+)\s+(\w+);\Z/)
7600 { # fields
7601 my ($TName, $FName) = ($2, $3);
7602 $TypeAttr{"Fields"}{$FName}{"Type"} = registerType($TName, $LibVersion);
7603 if($LINE=~/(\A|\s+)final\s+/) {
7604 $TypeAttr{"Fields"}{$FName}{"Final"} = 1;
7605 }
7606 if($LINE=~/(\A|\s+)static\s+/) {
7607 $TypeAttr{"Fields"}{$FName}{"Static"} = 1;
7608 }
7609 if($LINE=~/(\A|\s+)transient\s+/) {
7610 $TypeAttr{"Fields"}{$FName}{"Transient"} = 1;
7611 }
7612 if($LINE=~/(\A|\s+)volatile\s+/) {
7613 $TypeAttr{"Fields"}{$FName}{"Volatile"} = 1;
7614 }
7615 if($LINE=~/(\A|\s+)(public|protected|private)\s+/) {
7616 $TypeAttr{"Fields"}{$FName}{"Access"} = $2;
7617 }
7618 else {
7619 $TypeAttr{"Fields"}{$FName}{"Access"} = "package-private";
7620 }
7621 if($TypeAttr{"Fields"}{$FName}{"Access"}!~/private/) {
7622 $Class_Fields{$LibVersion}{$TypeAttr{"Name"}}{$FName}=$TypeAttr{"Fields"}{$FName}{"Type"};
7623 }
7624 $TypeAttr{"Fields"}{$FName}{"Pos"} = $FieldPos++;
7625
7626 # read the Signature
7627 if($Content[$LineNum++]=~/(Signature|descriptor):\s*(.+)\Z/i)
7628 {
7629 my $FSignature = $2;
7630 if(my $PackageName = get_SFormat($CurrentPackage)) {
7631 $TypeAttr{"Fields"}{$FName}{"Mangled"} = $PackageName."/".$CurrentClass.".".$FName.":".$FSignature;
7632 }
7633 }
7634 if($Content[$LineNum]=~/flags:/i)
7635 { # flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ANNOTATION
7636 $LineNum++;
7637 }
7638
7639 # read the Value
7640 if($Content[$LineNum]=~/Constant\s*value:\s*([^\s]+)\s(.*)\Z/i)
7641 {
7642 # Java 6: Constant value: ...
7643 # Java 7: ConstantValue: ...
7644 $LineNum+=1;
7645 my ($TName, $Value) = ($1, $2);
7646 if($Value)
7647 {
7648 if($Value=~s/Deprecated:\s*true\Z//g) {
7649 # deprecated values: ?
7650 }
7651 $TypeAttr{"Fields"}{$FName}{"Value"} = $Value;
7652 }
7653 elsif($TName eq "String") {
7654 $TypeAttr{"Fields"}{$FName}{"Value"} = "\@EMPTY_STRING\@";
7655 }
7656 }
7657 }
7658 elsif($LINE=~/(\A|\s+)(class|interface)\s+([^\s\{]+)(\s+|\{|\Z)/)
7659 { # properties of classes and interfaces
7660 if($TypeAttr{"Name"})
7661 { # register previous
7662 %{$TypeInfo{$LibVersion}{registerType($TypeAttr{"Name"}, $LibVersion)}} = %TypeAttr;
7663 }
7664
7665 %TypeAttr = ("Type"=>$2, "Name"=>$3); # reset previous class
7666 %AnnotationName = (); # reset annotations of the class
7667 %AnnotationNum = (); # support for Java 7
7668 $InAnnotations_Class = 1;
7669
7670 $FieldPos = 0; # reset field position
7671 $CurrentMethod = ""; # reset current method
7672 $TypeAttr{"Archive"} = $ArchiveName;
7673 if($TypeAttr{"Name"}=~/\A(.+)\.([^.]+)\Z/)
7674 {
7675 $CurrentClass = $2;
7676 $TypeAttr{"Package"} = $1;
7677 $CurrentPackage = $TypeAttr{"Package"};
7678 }
7679 else
7680 {
7681 $CurrentClass = $TypeAttr{"Name"};
7682 $CurrentPackage = "";
7683 }
7684 if($CurrentClass=~s/#/./g)
7685 { # javax.swing.text.GlyphView.GlyphPainter <=> GlyphView$GlyphPainter
7686 $TypeAttr{"Name"}=~s/#/./g;
7687 }
7688 if($LINE=~/(\A|\s+)(public|protected|private)\s+/) {
7689 $TypeAttr{"Access"} = $2;
7690 }
7691 else {
7692 $TypeAttr{"Access"} = "package-private";
7693 }
7694 if($LINE=~/\s+extends\s+([^\s\{]+)/)
7695 {
7696 my $Extended = $1;
7697
7698 if($TypeAttr{"Type"} eq "class")
7699 {
7700 if($Extended ne $CurrentPackage.".".$CurrentClass) {
7701 $TypeAttr{"SuperClass"} = registerType($Extended, $LibVersion);
7702 }
7703 }
7704 elsif($TypeAttr{"Type"} eq "interface")
7705 {
7706 my @Elems = separate_Params($Extended, 0, 0);
7707 foreach my $SuperInterface (@Elems)
7708 {
7709 if($SuperInterface ne $CurrentPackage.".".$CurrentClass) {
7710 $TypeAttr{"SuperInterface"}{registerType($SuperInterface, $LibVersion)} = 1;
7711 }
7712
7713 if($SuperInterface eq "java.lang.annotation.Annotation") {
7714 $TypeAttr{"Annotation"} = 1;
7715 }
7716 }
7717 }
7718 }
7719 if($LINE=~/\s+implements\s+([^\s\{]+)/)
7720 {
7721 my $Implemented = $1;
7722 my @Elems = separate_Params($Implemented, 0, 0);
7723
7724 foreach my $SuperInterface (@Elems) {
7725 $TypeAttr{"SuperInterface"}{registerType($SuperInterface, $LibVersion)} = 1;
7726 }
7727 }
7728 if($LINE=~/(\A|\s+)abstract\s+/) {
7729 $TypeAttr{"Abstract"} = 1;
7730 }
7731 if($LINE=~/(\A|\s+)final\s+/) {
7732 $TypeAttr{"Final"} = 1;
7733 }
7734 if($LINE=~/(\A|\s+)static\s+/) {
7735 $TypeAttr{"Static"} = 1;
7736 }
7737 }
7738 elsif(index($LINE, "Deprecated: true")!=-1
7739 or index($LINE, "Deprecated: length")!=-1)
7740 { # deprecated method or class
7741 if($CurrentMethod) {
7742 $MethodInfo{$LibVersion}{$CurrentMethod}{"Deprecated"} = 1;
7743 }
7744 elsif($CurrentClass) {
7745 $TypeAttr{"Deprecated"} = 1;
7746 }
7747 }
7748 elsif(index($LINE, "RuntimeInvisibleAnnotations")!=-1
7749 or index($LINE, "RuntimeVisibleAnnotations")!=-1)
7750 {
7751 $InAnnotations = 1;
7752 $InCode = 0;
7753 }
7754 elsif(defined $InAnnotations and index($LINE, "InnerClasses")!=-1) {
7755 $InAnnotations = undef;
7756 }
7757 else
7758 {
7759 # unparsed
7760 }
7761 }
7762 if($TypeAttr{"Name"})
7763 { # register last
7764 %{$TypeInfo{$LibVersion}{registerType($TypeAttr{"Name"}, $LibVersion)}} = %TypeAttr;
7765 }
7766}
7767
7768sub separate_Params($$$)
7769{
7770 my ($Params, $Comma, $Sp) = @_;
7771 my @Parts = ();
7772 my %B = ( "("=>0, "<"=>0, ")"=>0, ">"=>0 );
7773 my $Part = 0;
7774 foreach my $Pos (0 .. length($Params) - 1)
7775 {
7776 my $S = substr($Params, $Pos, 1);
7777 if(defined $B{$S}) {
7778 $B{$S} += 1;
7779 }
7780 if($S eq "," and
7781 $B{"("}==$B{")"} and $B{"<"}==$B{">"})
7782 {
7783 if($Comma)
7784 { # include comma
7785 $Parts[$Part] .= $S;
7786 }
7787 $Part += 1;
7788 }
7789 else {
7790 $Parts[$Part] .= $S;
7791 }
7792 }
7793 if(not $Sp)
7794 { # remove spaces
7795 foreach (@Parts)
7796 {
7797 s/\A //g;
7798 s/ \Z//g;
7799 }
7800 }
7801 return @Parts;
7802}
7803
7804sub registerUsage($$)
7805{
7806 my ($TypeId, $LibVersion) = @_;
7807 $Class_Constructed{$LibVersion}{$TypeId} = 1;
7808 if(my $BaseId = $TypeInfo{$LibVersion}{$TypeId}{"BaseType"}) {
7809 $Class_Constructed{$LibVersion}{$BaseId} = 1;
7810 }
7811}
7812
7813sub checkVoidMethod($)
7814{
7815 my $Method = $_[0];
7816 return "" if(not $Method);
7817 if($Method=~s/\)(.+)\Z/\)V/g) {
7818 return $Method;
7819 }
7820 else {
7821 return "";
7822 }
7823}
7824
7825sub detectAdded()
7826{
7827 foreach my $Method (keys(%{$MethodInfo{2}}))
7828 {
7829 if(not defined $MethodInfo{1}{$Method})
7830 {
7831 if(not methodFilter($Method, 2)) {
7832 next;
7833 }
7834
7835 my $ClassId = $MethodInfo{2}{$Method}{"Class"};
7836 my %Class = get_Type($ClassId, 2);
7837
7838 $CheckedTypes{$Class{"Name"}} = 1;
7839 $CheckedMethods{$Method} = 1;
7840
7841 if(not $MethodInfo{2}{$Method}{"Constructor"}
7842 and my $Overridden = findMethod($Method, 2, $Class{"Name"}, 2))
7843 {
7844 if(defined $MethodInfo{1}{$Overridden}
7845 and get_TypeType($ClassId, 2) eq "class" and $TName_Tid{1}{$Class{"Name"}})
7846 { # class should exist in previous version
7847 %{$CompatProblems{$Overridden}{"Class_Overridden_Method"}{"this.".get_SFormat($Method)}}=(
7848 "Type_Name"=>$Class{"Name"},
7849 "Target"=>$MethodInfo{2}{$Method}{"Signature"},
7850 "Old_Value"=>$Overridden,
7851 "New_Value"=>$Method );
7852 }
7853 }
7854 if($MethodInfo{2}{$Method}{"Abstract"}) {
7855 $AddedMethod_Abstract{$Class{"Name"}}{$Method} = 1;
7856 }
7857
7858 %{$CompatProblems{$Method}{"Added_Method"}{""}}=();
7859
7860 if(not $MethodInfo{2}{$Method}{"Constructor"})
7861 {
7862 if(get_TypeName($MethodInfo{2}{$Method}{"Return"}, 2) ne "void"
7863 and my $VoidMethod = checkVoidMethod($Method))
7864 {
7865 if(defined $MethodInfo{1}{$VoidMethod})
7866 { # return value type changed from "void" to
7867 $ChangedReturnFromVoid{$VoidMethod} = 1;
7868 $ChangedReturnFromVoid{$Method} = 1;
7869
7870 %{$CompatProblems{$VoidMethod}{"Changed_Method_Return_From_Void"}{""}}=(
7871 "New_Value"=>get_TypeName($MethodInfo{2}{$Method}{"Return"}, 2)
7872 );
7873 }
7874 }
7875 }
7876 }
7877 }
7878}
7879
7880sub detectRemoved()
7881{
7882 foreach my $Method (keys(%{$MethodInfo{1}}))
7883 {
7884 if(not defined $MethodInfo{2}{$Method})
7885 {
7886 if(not methodFilter($Method, 1)) {
7887 next;
7888 }
7889
7890 my $ClassId = $MethodInfo{1}{$Method}{"Class"};
7891 my %Class = get_Type($ClassId, 1);
7892
7893 $CheckedTypes{$Class{"Name"}} = 1;
7894 $CheckedMethods{$Method} = 1;
7895
7896 if(not $MethodInfo{1}{$Method}{"Constructor"}
7897 and my $MovedUp = findMethod($Method, 1, $Class{"Name"}, 2))
7898 {
7899 if(get_TypeType($ClassId, 1) eq "class"
7900 and not $MethodInfo{1}{$Method}{"Abstract"} and $TName_Tid{2}{$Class{"Name"}})
7901 {# class should exist in newer version
7902 %{$CompatProblems{$Method}{"Class_Method_Moved_Up_Hierarchy"}{"this.".get_SFormat($MovedUp)}}=(
7903 "Type_Name"=>$Class{"Name"},
7904 "Target"=>$MethodInfo{2}{$MovedUp}{"Signature"},
7905 "Old_Value"=>$Method,
7906 "New_Value"=>$MovedUp );
7907 }
7908 }
7909 else
7910 {
7911 if($MethodInfo{1}{$Method}{"Abstract"}) {
7912 $RemovedMethod_Abstract{$Class{"Name"}}{$Method} = 1;
7913 }
7914 %{$CompatProblems{$Method}{"Removed_Method"}{""}}=();
7915 }
7916 }
7917 }
7918}
7919
7920sub getArchives($)
7921{
7922 my $LibVersion = $_[0];
7923 my @Paths = ();
7924 foreach my $Path (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"Archives"}))
7925 {
7926 if(not -e $Path) {
7927 exitStatus("Access_Error", "can't access \'$Path\'");
7928 }
7929 foreach (getArchivePaths($Path, $LibVersion)) {
7930 push(@Paths, $_);
7931 }
7932 }
7933 return @Paths;
7934}
7935
7936sub getArchivePaths($$)
7937{
7938 my ($Dest, $LibVersion) = @_;
7939 if(-f $Dest) {
7940 return ($Dest);
7941 }
7942 elsif(-d $Dest)
7943 {
7944 $Dest=~s/[\/\\]+\Z//g;
7945 my @AllClasses = ();
7946 foreach my $Path (cmd_find($Dest,"","*\.jar",""))
7947 {
7948 next if(ignore_path($Path, $Dest));
7949 push(@AllClasses, resolve_symlink($Path));
7950 }
7951 return @AllClasses;
7952 }
7953 return ();
7954}
7955
7956sub isCyclical($$)
7957{
7958 return (grep {$_ eq $_[1]} @{$_[0]});
7959}
7960
7961sub read_symlink($)
7962{
7963 my $Path = $_[0];
7964 return "" if(not $Path or not -f $Path);
7965 return $Cache{"read_symlink"}{$Path} if(defined $Cache{"read_symlink"}{$Path});
7966 if(my $ReadlinkCmd = get_CmdPath("readlink"))
7967 {
7968 my $Res = `$ReadlinkCmd -n \"$Path\"`;
7969 return ($Cache{"read_symlink"}{$Path} = $Res);
7970 }
7971 elsif(my $FileCmd = get_CmdPath("file"))
7972 {
7973 my $Info = `$FileCmd \"$Path\"`;
7974 if($Info=~/symbolic\s+link\s+to\s+['`"]*([\w\d\.\-\/\\]+)['`"]*/i) {
7975 return ($Cache{"read_symlink"}{$Path} = $1);
7976 }
7977 }
7978 return ($Cache{"read_symlink"}{$Path} = "");
7979}
7980
7981sub resolve_symlink($)
7982{
7983 my $Path = $_[0];
7984 return "" if(not $Path or not -f $Path);
7985 return $Path if(isCyclical(\@RecurSymlink, $Path));
7986 push(@RecurSymlink, $Path);
7987 if(-l $Path and my $Redirect=read_symlink($Path))
7988 {
7989 if(is_abs($Redirect))
7990 {
7991 my $Res = resolve_symlink($Redirect);
7992 pop(@RecurSymlink);
7993 return $Res;
7994 }
7995 elsif($Redirect=~/\.\.[\/\\]/)
7996 {
7997 $Redirect = joinPath(get_dirname($Path),$Redirect);
7998 while($Redirect=~s&(/|\\)[^\/\\]+(\/|\\)\.\.(\/|\\)&$1&){};
7999 my $Res = resolve_symlink($Redirect);
8000 pop(@RecurSymlink);
8001 return $Res;
8002 }
8003 elsif(-f get_dirname($Path)."/".$Redirect)
8004 {
8005 my $Res = resolve_symlink(joinPath(get_dirname($Path),$Redirect));
8006 pop(@RecurSymlink);
8007 return $Res;
8008 }
8009 return $Path;
8010 }
8011 else
8012 {
8013 pop(@RecurSymlink);
8014 return $Path;
8015 }
8016}
8017
8018sub cmpVersions($$)
8019{# compare two version strings in dotted-numeric format
8020 my ($V1, $V2) = @_;
8021 return 0 if($V1 eq $V2);
8022 my @V1Parts = split(/\./, $V1);
8023 my @V2Parts = split(/\./, $V2);
8024 for (my $i = 0; $i <= $#V1Parts && $i <= $#V2Parts; $i++) {
8025 return -1 if(int($V1Parts[$i]) < int($V2Parts[$i]));
8026 return 1 if(int($V1Parts[$i]) > int($V2Parts[$i]));
8027 }
8028 return -1 if($#V1Parts < $#V2Parts);
8029 return 1 if($#V1Parts > $#V2Parts);
8030 return 0;
8031}
8032
8033sub majorVersion($)
8034{
8035 my $Version = $_[0];
8036 return 0 if(not $Version);
8037 my @VParts = split(/\./, $Version);
8038 return $VParts[0];
8039}
8040
8041sub isDump($)
8042{
8043 if($_[0]=~/\A(.+)\.(api|dump|apidump)(\Q.tar.gz\E|\Q.zip\E|)\Z/)
8044 { # returns a name of package
8045 return $1;
8046 }
8047 return 0;
8048}
8049
8050sub isDumpFile($)
8051{
8052 if($_[0]=~/\.(api|dump|apidump)\Z/)
8053 {
8054 return 1;
8055 }
8056 return 0;
8057}
8058
8059sub read_API_Dump($$$)
8060{
8061 my ($LibVersion, $Path, $Subj) = @_;
8062 return if(not $LibVersion or not -e $Path);
8063
8064 my $FilePath = unpackDump($Path);
8065 if(not isDumpFile($FilePath)) {
8066 exitStatus("Invalid_Dump", "specified API dump \'$Path\' is not valid, try to recreate it");
8067 }
8068 my $Content = readFile($FilePath);
8069 rmtree($TMP_DIR."/unpack");
8070
8071 if($Content!~/};\s*\Z/) {
8072 exitStatus("Invalid_Dump", "specified API dump \'$Path\' is not valid, try to recreate it");
8073 }
8074 my $APIDump = eval($Content);
8075 if(not $APIDump) {
8076 exitStatus("Error", "internal error - eval() procedure seem to not working correctly, try to remove 'use strict' and try again");
8077 }
8078 my $DumpVersion = $APIDump->{"API_DUMP_VERSION"};
8079 if(majorVersion($DumpVersion) ne $API_DUMP_MAJOR)
8080 { # compatible with the dumps of the same major version
8081 exitStatus("Dump_Version", "incompatible version $DumpVersion of specified API dump (allowed only $API_DUMP_MAJOR.0<=V<=$API_DUMP_MAJOR.9)");
8082 }
8083
8084 if(defined $TypeInfo{$LibVersion})
8085 {
8086 foreach my $TId (keys(%{$APIDump->{"TypeInfo"}}))
8087 {
8088 $TypeInfo{$LibVersion}{$TId} = $APIDump->{"TypeInfo"}{$TId};
8089 }
8090 }
8091 else {
8092 $TypeInfo{$LibVersion} = $APIDump->{"TypeInfo"};
8093 }
8094
8095 foreach my $TypeId (keys(%{$APIDump->{"TypeInfo"}}))
8096 {
8097 my %TypeAttr = %{$TypeInfo{$LibVersion}{$TypeId}};
8098 $TName_Tid{$LibVersion}{$TypeAttr{"Name"}} = $TypeId;
8099
8100 if($Subj ne "Dep")
8101 {
8102 if(my $Archive = $TypeAttr{"Archive"}) {
8103 $LibArchives{$LibVersion}{$Archive} = 1;
8104 }
8105 }
8106
8107 foreach my $FieldName (keys(%{$TypeAttr{"Fields"}}))
8108 {
8109 if($TypeAttr{"Fields"}{$FieldName}{"Access"}=~/public|protected/) {
8110 $Class_Fields{$LibVersion}{$TypeAttr{"Name"}}{$FieldName} = $TypeAttr{"Fields"}{$FieldName}{"Type"};
8111 }
8112 }
8113
8114 if($Subj eq "Dep") {
8115 $TypeInfo{$LibVersion}{$TypeId}{"Dep"} = 1;
8116 }
8117 }
8118 my $MInfo = $APIDump->{"MethodInfo"};
8119 foreach my $M_Id (keys(%{$MInfo}))
8120 {
8121 my $Name = $MInfo->{$M_Id}{"Name"};
8122 $MethodInfo{$LibVersion}{$Name} = $MInfo->{$M_Id};
8123
8124 if($Subj eq "Dep") {
8125 $MethodInfo{$LibVersion}{$Name}{"Dep"} = 1;
8126 }
8127 }
8128
8129 my $MUsed = $APIDump->{"MethodUsed"};
8130 foreach my $M_Id (keys(%{$MUsed}))
8131 {
8132 my $Name = $MUsed->{$M_Id}{"Name"};
8133 $MethodUsed{$LibVersion}{$Name} = $MUsed->{$M_Id}{"Used"};
8134 }
8135
8136 foreach my $Method (keys(%{$MethodInfo{$LibVersion}}))
8137 {
8138 if(my $ClassId = $MethodInfo{$LibVersion}{$Method}{"Class"}
8139 and $MethodInfo{$LibVersion}{$Method}{"Access"}=~/public|protected/)
8140 {
8141 $Class_Methods{$LibVersion}{get_TypeName($ClassId, $LibVersion)}{$Method} = 1;
8142 if($MethodInfo{$LibVersion}{$Method}{"Abstract"}) {
8143 $Class_AbstractMethods{$LibVersion}{get_TypeName($ClassId, $LibVersion)}{$Method} = 1;
8144 }
8145 }
8146 }
8147
8148 # $FieldUsed{$LibVersion} = $APIDump->{"FieldUsed"};
8149
8150 if(keys(%{$LibArchives{$LibVersion}})) {
8151 $Descriptor{$LibVersion}{"Archives"} = "OK";
8152 }
8153 $Descriptor{$LibVersion}{"Version"} = $APIDump->{"LibraryVersion"};
8154 $Descriptor{$LibVersion}{"Dump"} = 1;
8155}
8156
8157sub createDescriptor($$)
8158{
8159 my ($LibVersion, $Path) = @_;
8160 return if(not $LibVersion or not $Path or not -e $Path);
8161
8162 if(isDump($Path))
8163 { # API dump
8164 read_API_Dump($LibVersion, $Path, "Main");
8165 }
8166 else
8167 {
8168 if(-d $Path or $Path=~/\.jar\Z/)
8169 {
8170 readDescriptor($LibVersion,"
8171 <version>
8172 ".$TargetVersion{$LibVersion}."
8173 </version>
8174
8175 <archives>
8176 $Path
8177 </archives>");
8178 }
8179 else
8180 { # standard XML descriptor
8181 readDescriptor($LibVersion, readFile($Path));
8182 }
8183 }
8184
8185 if(my $Dep = $DepDump{$LibVersion}) {
8186 read_API_Dump($LibVersion, $Dep, "Dep");
8187 }
8188}
8189
8190sub get_version($)
8191{
8192 my $Cmd = $_[0];
8193 return "" if(not $Cmd);
8194 my $Version = `$Cmd --version 2>\"$TMP_DIR/null\"`;
8195 return $Version;
8196}
8197
8198sub get_depth($)
8199{
8200 if(defined $Cache{"get_depth"}{$_[0]}) {
8201 return $Cache{"get_depth"}{$_[0]}
8202 }
8203 return ($Cache{"get_depth"}{$_[0]} = ($_[0]=~tr![\/\\]|\:\:!!));
8204}
8205
8206sub show_time_interval($)
8207{
8208 my $Interval = $_[0];
8209 my $Hr = int($Interval/3600);
8210 my $Min = int($Interval/60)-$Hr*60;
8211 my $Sec = $Interval-$Hr*3600-$Min*60;
8212 if($Hr) {
8213 return "$Hr hr, $Min min, $Sec sec";
8214 }
8215 elsif($Min) {
8216 return "$Min min, $Sec sec";
8217 }
8218 else {
8219 return "$Sec sec";
8220 }
8221}
8222
8223sub checkVersionNum($$)
8224{
8225 my ($LibVersion, $Path) = @_;
8226
8227 if($TargetVersion{$LibVersion}) {
8228 return;
8229 }
8230
8231 if($Path!~/\.jar\Z/i) {
8232 return;
8233 }
8234
8235 my $Ver = undef;
8236
8237 if(not defined $Ver) {
8238 $Ver = getManifestVersion(get_abs_path($Path));
8239 }
8240
8241 if(not defined $Ver) {
8242 $Ver = getPkgVersion(get_filename($Path));
8243 }
8244
8245 if(not defined $Ver) {
8246 $Ver = parseVersion($Path);
8247 }
8248
8249 if(not defined $Ver)
8250 {
8251 if($DumpAPI)
8252 {
8253 $Ver = "XYZ";
8254 }
8255 else
8256 {
8257 if($LibVersion==1) {
8258 $Ver = "X";
8259 }
8260 else {
8261 $Ver = "Y";
8262 }
8263 }
8264 }
8265
8266 $TargetVersion{$LibVersion} = $Ver;
8267
8268 if($DumpAPI) {
8269 printMsg("WARNING", "set version number to $Ver (use -vnum option to change it)");
8270 }
8271 else {
8272 printMsg("WARNING", "set #$LibVersion version number to $Ver (use --v$LibVersion=NUM option to change it)");
8273 }
8274}
8275
8276sub getManifestVersion($)
8277{
8278 my $Path = $_[0];
8279
8280 if(not $Path or not -e $Path) {
8281 return undef;
8282 }
8283
8284 my $JarCmd = get_CmdPath("jar");
8285 if(not $JarCmd) {
8286 exitStatus("Not_Found", "can't find \"jar\" command");
8287 }
8288 chdir($TMP_DIR);
8289 system($JarCmd." -xf \"$Path\" META-INF 2>null");
8290 chdir($ORIG_DIR);
8291 if(my $Content = readFile("$TMP_DIR/META-INF/MANIFEST.MF"))
8292 {
8293 if($Content=~/(\A|\s)Implementation\-Version:\s*(.+)(\s|\Z)/i) {
8294 return $2;
8295 }
8296 }
8297 return undef;
8298}
8299
8300sub parseVersion($)
8301{
8302 my $Str = $_[0];
8303
8304 if(not $Str) {
8305 return undef;
8306 }
8307
8308 if($Str=~/(\/|\\|\w|\A)[\-\_]*(\d+[\d\.\-]+\d+|\d+)/) {
8309 return $2;
8310 }
8311 return undef;
8312}
8313
8314sub getPkgVersion($)
8315{
8316 my $Name = $_[0];
8317 $Name=~s/\.\w+\Z//;
8318 if($Name=~/\A(.+[a-z])[\-\_](v|ver|)(\d.+?)\Z/i)
8319 { # libsample-N
8320 # libsample-vN
8321 return ($1, $3);
8322 }
8323 elsif($Name=~/\A(.+?)(\d[\d\.]*)\Z/i)
8324 { # libsampleN
8325 return ($1, $2);
8326 }
8327 elsif($Name=~/\A(.+)[\-\_](v|ver|)(\d.+?)\Z/i)
8328 { # libsample-N
8329 # libsample-vN
8330 return ($1, $3);
8331 }
8332 elsif($Name=~/\A([a-z_\-]+)(\d.+?)\Z/i)
8333 { # libsampleNb
8334 return ($1, $2);
8335 }
8336 return (undef, undef);
8337}
8338
8339sub get_OSgroup()
8340{
8341 if($Config{"osname"}=~/macos|darwin|rhapsody/i) {
8342 return "macos";
8343 }
8344 elsif($Config{"osname"}=~/freebsd|openbsd|netbsd/i) {
8345 return "bsd";
8346 }
8347 elsif($Config{"osname"}=~/haiku|beos/i) {
8348 return "beos";
8349 }
8350 elsif($Config{"osname"}=~/symbian|epoc/i) {
8351 return "symbian";
8352 }
8353 elsif($Config{"osname"}=~/win/i) {
8354 return "windows";
8355 }
8356 else {
8357 return $Config{"osname"};
8358 }
8359}
8360
8361sub get_ARG_MAX()
8362{
8363 if($OSgroup eq "windows") {
8364 return 1990; # 8191, 2047
8365 }
8366 else
8367 { # Linux
8368 # TODO: set max possible value (~131000)
8369 return 32767;
8370 }
8371}
8372
8373sub dump_sorting($)
8374{
8375 my $Hash = $_[0];
8376 return [] if(not $Hash);
8377 my @Keys = keys(%{$Hash});
8378 return [] if($#Keys<0);
8379 if($Keys[0]=~/\A\d+\Z/)
8380 { # numbers
8381 return [sort {int($a)<=>int($b)} @Keys];
8382 }
8383 else
8384 { # strings
8385 return [sort {$a cmp $b} @Keys];
8386 }
8387}
8388
8389sub detect_bin_default_paths()
8390{
8391 my $EnvPaths = $ENV{"PATH"};
8392 if($OSgroup eq "beos") {
8393 $EnvPaths.=":".$ENV{"BETOOLS"};
8394 }
8395 elsif($OSgroup eq "windows"
8396 and my $JHome = $ENV{"JAVA_HOME"}) {
8397 $EnvPaths.=";$JHome\\bin";
8398 }
8399 my $Sep = ($OSgroup eq "windows")?";":":|;";
8400 foreach my $Path (sort {length($a)<=>length($b)} split(/$Sep/, $EnvPaths))
8401 {
8402 $Path=~s/[\/\\]+\Z//g;
8403 next if(not $Path);
8404 $DefaultBinPaths{$Path} = 1;
8405 }
8406}
8407
8408sub detect_default_paths()
8409{
8410 if(keys(%SystemPaths))
8411 {# run once
8412 return;
8413 }
8414
8415 foreach my $Type (keys(%{$OS_AddPath{$OSgroup}}))
8416 {# additional search paths
8417 foreach my $Path (keys(%{$OS_AddPath{$OSgroup}{$Type}}))
8418 {
8419 next if(not -d $Path);
8420 $SystemPaths{$Type}{$Path} = $OS_AddPath{$OSgroup}{$Type}{$Path};
8421 }
8422 }
8423 if($OSgroup ne "windows")
8424 {
8425 foreach my $Type ("include", "lib", "bin")
8426 {# autodetecting system "devel" directories
8427 foreach my $Path (cmd_find("/","d","*$Type*",1)) {
8428 $SystemPaths{$Type}{$Path} = 1;
8429 }
8430 if(-d "/usr") {
8431 foreach my $Path (cmd_find("/usr","d","*$Type*",1)) {
8432 $SystemPaths{$Type}{$Path} = 1;
8433 }
8434 }
8435 }
8436 }
8437 detect_bin_default_paths();
8438 foreach my $Path (keys(%DefaultBinPaths)) {
8439 $SystemPaths{"bin"}{$Path} = $DefaultBinPaths{$Path};
8440 }
8441
8442 if(not $TestSystem)
8443 {
8444 if(my $JavacCmd = get_CmdPath("javac"))
8445 {
8446 if(my $Ver = `$JavacCmd -version 2>&1`)
8447 {
8448 if($Ver=~/javac\s+(.+)/) {
8449 printMsg("INFO", "using Java ".$1);
8450 }
8451 }
8452 }
8453 }
8454}
8455
8456sub exitStatus($$)
8457{
8458 my ($Code, $Msg) = @_;
8459 print STDERR "ERROR: ". $Msg."\n";
8460 exit($ERROR_CODE{$Code});
8461}
8462
8463sub printMsg($$)
8464{
8465 my ($Type, $Msg) = @_;
8466 if($Type!~/\AINFO/) {
8467 $Msg = $Type.": ".$Msg;
8468 }
8469 if($Type!~/_C\Z/) {
8470 $Msg .= "\n";
8471 }
8472 if($Type eq "ERROR") {
8473 print STDERR $Msg;
8474 }
8475 else {
8476 print $Msg;
8477 }
8478}
8479
8480sub printStatMsg($)
8481{
8482 my $Level = $_[0];
8483 printMsg("INFO", "total ".lc($Level)." compatibility problems: ".$RESULT{$Level}{"Problems"}.", warnings: ".$RESULT{$Level}{"Warnings"});
8484}
8485
8486sub printReport()
8487{
8488 printMsg("INFO", "creating compatibility report ...");
8489 createReport();
8490 if($JoinReport or $DoubleReport)
8491 {
8492 if($RESULT{"Binary"}{"Problems"}
8493 or $RESULT{"Source"}{"Problems"})
8494 {
8495 printMsg("INFO", "binary compatibility: ".(100-$RESULT{"Binary"}{"Affected"})."\%");
8496 printMsg("INFO", "source compatibility: ".(100-$RESULT{"Source"}{"Affected"})."\%");
8497 }
8498 else
8499 {
8500 printMsg("INFO", "binary compatibility: 100\%");
8501 printMsg("INFO", "source compatibility: 100\%");
8502 }
8503 printStatMsg("Binary");
8504 printStatMsg("Source");
8505 }
8506 elsif($BinaryOnly)
8507 {
8508 if($RESULT{"Binary"}{"Problems"}) {
8509 printMsg("INFO", "binary compatibility: ".(100-$RESULT{"Binary"}{"Affected"})."\%");
8510 }
8511 else {
8512 printMsg("INFO", "binary compatibility: 100\%");
8513 }
8514 printStatMsg("Binary");
8515 }
8516 elsif($SourceOnly)
8517 {
8518 if($RESULT{"Source"}{"Problems"}) {
8519 printMsg("INFO", "source compatibility: ".(100-$RESULT{"Source"}{"Affected"})."\%");
8520 }
8521 else {
8522 printMsg("INFO", "source compatibility: 100\%");
8523 }
8524 printStatMsg("Source");
8525 }
8526 if($JoinReport)
8527 {
8528 printMsg("INFO", "report: ".getReportPath("Join"));
8529 }
8530 elsif($DoubleReport)
8531 { # default
8532 printMsg("INFO", "report (BC): ".getReportPath("Binary"));
8533 printMsg("INFO", "report (SC): ".getReportPath("Source"));
8534 }
8535 elsif($BinaryOnly)
8536 { # --binary
8537 printMsg("INFO", "report: ".getReportPath("Binary"));
8538 }
8539 elsif($SourceOnly)
8540 { # --source
8541 printMsg("INFO", "report: ".getReportPath("Source"));
8542 }
8543}
8544
8545sub getReportPath($)
8546{
8547 my $Level = $_[0];
8548 my $Dir = "compat_reports/$TargetLibraryName/".$Descriptor{1}{"Version"}."_to_".$Descriptor{2}{"Version"};
8549 if($Level eq "Binary")
8550 {
8551 if($BinaryReportPath)
8552 { # --bin-report-path
8553 return $BinaryReportPath;
8554 }
8555 elsif($OutputReportPath)
8556 { # --report-path
8557 return $OutputReportPath;
8558 }
8559 else
8560 { # default
8561 return $Dir."/bin_compat_report.html";
8562 }
8563 }
8564 elsif($Level eq "Source")
8565 {
8566 if($SourceReportPath)
8567 { # --src-report-path
8568 return $SourceReportPath;
8569 }
8570 elsif($OutputReportPath)
8571 { # --report-path
8572 return $OutputReportPath;
8573 }
8574 else
8575 { # default
8576 return $Dir."/src_compat_report.html";
8577 }
8578 }
8579 else
8580 {
8581 if($OutputReportPath)
8582 { # --report-path
8583 return $OutputReportPath;
8584 }
8585 else
8586 { # default
8587 return $Dir."/compat_report.html";
8588 }
8589 }
8590}
8591
8592sub initLogging($)
8593{
8594 my $LibVersion = $_[0];
8595 if($Debug)
8596 { # debug directory
8597 $DEBUG_PATH{$LibVersion} = "debug/$TargetLibraryName/".$Descriptor{$LibVersion}{"Version"};
8598
8599 if(-d $DEBUG_PATH{$LibVersion}) {
8600 rmtree($DEBUG_PATH{$LibVersion});
8601 }
8602 }
8603}
8604
8605sub createArchive($$)
8606{
8607 my ($Path, $To) = @_;
8608 if(not $To) {
8609 $To = ".";
8610 }
8611 if(not $Path or not -e $Path
8612 or not -d $To) {
8613 return "";
8614 }
8615 my ($From, $Name) = separate_path($Path);
8616 if($OSgroup eq "windows")
8617 { # *.zip
8618 my $ZipCmd = get_CmdPath("zip");
8619 if(not $ZipCmd) {
8620 exitStatus("Not_Found", "can't find \"zip\"");
8621 }
8622 my $Pkg = $To."/".$Name.".zip";
8623 unlink($Pkg);
8624 chdir($To);
8625 system("$ZipCmd -j \"$Name.zip\" \"$Path\" >\"$TMP_DIR/null\"");
8626 if($?)
8627 { # cannot allocate memory (or other problems with "zip")
8628 unlink($Path);
8629 exitStatus("Error", "can't pack the API dump: ".$!);
8630 }
8631 chdir($ORIG_DIR);
8632 unlink($Path);
8633 return $Pkg;
8634 }
8635 else
8636 { # *.tar.gz
8637 my $TarCmd = get_CmdPath("tar");
8638 if(not $TarCmd) {
8639 exitStatus("Not_Found", "can't find \"tar\"");
8640 }
8641 my $GzipCmd = get_CmdPath("gzip");
8642 if(not $GzipCmd) {
8643 exitStatus("Not_Found", "can't find \"gzip\"");
8644 }
8645 my $Pkg = abs_path($To)."/".$Name.".tar.gz";
8646 unlink($Pkg);
8647 chdir($From);
8648 system($TarCmd, "-czf", $Pkg, $Name);
8649 if($?)
8650 { # cannot allocate memory (or other problems with "tar")
8651 unlink($Path);
8652 exitStatus("Error", "can't pack the API dump: ".$!);
8653 }
8654 chdir($ORIG_DIR);
8655 unlink($Path);
8656 return $To."/".$Name.".tar.gz";
8657 }
8658}
8659
8660sub getMd5(@)
8661{
8662 my $Md5 = md5_hex(@_);
8663 return substr($Md5, 0, $MD5_LEN);
8664}
8665
8666sub scenario()
8667{
8668 if($BinaryOnly and $SourceOnly)
8669 { # both --binary and --source
8670 # is the default mode
8671 $DoubleReport = 1;
8672 $JoinReport = 0;
8673 $BinaryOnly = 0;
8674 $SourceOnly = 0;
8675 if($OutputReportPath)
8676 { # --report-path
8677 $DoubleReport = 0;
8678 $JoinReport = 1;
8679 }
8680 }
8681 elsif($BinaryOnly or $SourceOnly)
8682 { # --binary or --source
8683 $DoubleReport = 0;
8684 $JoinReport = 0;
8685 }
8686 if(defined $Help)
8687 {
8688 HELP_MESSAGE();
8689 exit(0);
8690 }
8691 if(defined $ShowVersion)
8692 {
8693 printMsg("INFO", "Java API Compliance Checker (JAPICC) $TOOL_VERSION\nCopyright (C) 2016 Andrey Ponomarenko's ABI Laboratory\nLicense: LGPL or GPL <http://www.gnu.org/licenses/>\nThis program is free software: you can redistribute it and/or modify it.\n\nWritten by Andrey Ponomarenko.");
8694 exit(0);
8695 }
8696 if(defined $DumpVersion)
8697 {
8698 printMsg("INFO", $TOOL_VERSION);
8699 exit(0);
8700 }
8701 $Data::Dumper::Sortkeys = 1;
8702
8703 # FIXME: can't pass \&dump_sorting - cause a segfault sometimes
8704 if($SortDump)
8705 {
8706 $Data::Dumper::Useperl = 1;
8707 $Data::Dumper::Sortkeys = \&dump_sorting;
8708 }
8709
8710 if(defined $TestSystem)
8711 {
8712 detect_default_paths();
8713 testSystem();
8714 exit(0);
8715 }
8716
8717 if(defined $ShortMode)
8718 {
8719 if(not defined $AffectLimit) {
8720 $AffectLimit = 10;
8721 }
8722 }
8723
8724 if(not $TargetLibraryName and not $CountMethods)
8725 {
8726 if($DumpAPI)
8727 {
8728 if($DumpAPI=~/\.jar\Z/)
8729 { # short usage
8730 my ($Name, $Version) = getPkgVersion(get_filename($DumpAPI));
8731 if($Name and $Version ne "")
8732 {
8733 $TargetLibraryName = $Name;
8734 if(not $TargetVersion{1}) {
8735 $TargetVersion{1} = $Version;
8736 }
8737 }
8738 }
8739 }
8740 else
8741 {
8742 if($Descriptor{1}{"Path"}=~/\.jar\Z/ and $Descriptor{2}{"Path"}=~/\.jar\Z/)
8743 { # short usage
8744 my ($Name1, $Version1) = getPkgVersion(get_filename($Descriptor{1}{"Path"}));
8745 my ($Name2, $Version2) = getPkgVersion(get_filename($Descriptor{2}{"Path"}));
8746
8747 if($Name1 and $Version1 ne ""
8748 and $Version2 ne "")
8749 {
8750 $TargetLibraryName = $Name1;
8751 if(not $TargetVersion{1}) {
8752 $TargetVersion{1} = $Version1;
8753 }
8754 if(not $TargetVersion{2}) {
8755 $TargetVersion{2} = $Version2;
8756 }
8757 }
8758 }
8759 }
8760
8761 if(not $TargetLibraryName) {
8762 exitStatus("Error", "library name is not selected (option --lib=NAME)");
8763 }
8764 }
8765 else
8766 { # validate library name
8767 if($TargetLibraryName=~/[\*\/\\]/) {
8768 exitStatus("Error", "\"\\\", \"\/\" and \"*\" symbols are not allowed in the library name");
8769 }
8770 }
8771 if(not $TargetTitle) {
8772 $TargetTitle = $TargetLibraryName;
8773 }
8774 if($ClassListPath)
8775 {
8776 if(not -f $ClassListPath) {
8777 exitStatus("Access_Error", "can't access file \'$ClassListPath\'");
8778 }
8779 foreach my $Class (split(/\n/, readFile($ClassListPath)))
8780 {
8781 $Class=~s/\//./g;
8782 $ClassList_User{$Class} = 1;
8783 }
8784 }
8785 if($AnnotationsListPath)
8786 {
8787 if(not -f $AnnotationsListPath) {
8788 exitStatus("Access_Error", "can't access file \'$AnnotationsListPath\'");
8789 }
8790 foreach my $Annotation (split(/\n/, readFile($AnnotationsListPath)))
8791 {
8792 $AnnotationList_User{$Annotation} = 1;
8793 }
8794 }
8795 if($SkipAnnotationsListPath)
8796 {
8797 if(not -f $SkipAnnotationsListPath) {
8798 exitStatus("Access_Error", "can't access file \'$SkipAnnotationsListPath\'");
8799 }
8800 foreach my $Annotation (split(/\n/, readFile($SkipAnnotationsListPath)))
8801 {
8802 $SkipAnnotationList_User{$Annotation} = 1;
8803 }
8804 }
8805 if($SkipClassesList)
8806 {
8807 if(not -f $SkipClassesList) {
8808 exitStatus("Access_Error", "can't access file \'$SkipClassesList\'");
8809 }
8810 foreach my $Class (split(/\n/, readFile($SkipClassesList)))
8811 {
8812 $Class=~s/\//./g;
8813 $SkipClasses{$Class} = 1;
8814 }
8815 }
8816 if($SkipPackagesList)
8817 {
8818 if(not -f $SkipPackagesList) {
8819 exitStatus("Access_Error", "can't access file \'$SkipPackagesList\'");
8820 }
8821 foreach my $Package (split(/\n/, readFile($SkipPackagesList)))
8822 {
8823 $SkipPackages{1}{$Package} = 1;
8824 $SkipPackages{2}{$Package} = 1;
8825 }
8826 }
8827 if($ClientPath)
8828 {
8829 if($ClientPath=~/\.class\Z/) {
8830 exitStatus("Error", "input file is not a java archive");
8831 }
8832
8833 if(-f $ClientPath)
8834 {
8835 detect_default_paths();
8836 readArchive(0, $ClientPath)
8837 }
8838 else {
8839 exitStatus("Access_Error", "can't access file \'$ClientPath\'");
8840 }
8841 }
8842
8843 if($CountMethods)
8844 {
8845 if(not -e $CountMethods) {
8846 exitStatus("Access_Error", "can't access \'$CountMethods\'");
8847 }
8848
8849 read_API_Dump(1, $CountMethods, "Main");
8850
8851 my $Count = 0;
8852 foreach my $Method (keys(%{$MethodInfo{1}})) {
8853 $Count += methodFilter($Method, 1);
8854 }
8855
8856 printMsg("INFO", $Count);
8857 exit(0);
8858 }
8859
8860 if($DumpAPI)
8861 {
8862 if(not -e $DumpAPI) {
8863 exitStatus("Access_Error", "can't access \'$DumpAPI\'");
8864 }
8865
8866 detect_default_paths();
8867 checkVersionNum(1, $DumpAPI);
8868
8869 my $TarCmd = get_CmdPath("tar");
8870 if(not $TarCmd) {
8871 exitStatus("Not_Found", "can't find \"tar\"");
8872 }
8873 my $GzipCmd = get_CmdPath("gzip");
8874 if(not $GzipCmd) {
8875 exitStatus("Not_Found", "can't find \"gzip\"");
8876 }
8877 createDescriptor(1, $DumpAPI);
8878 if(not $Descriptor{1}{"Archives"}) {
8879 exitStatus("Error", "descriptor does not contain Java ARchives");
8880 }
8881
8882 initLogging(1);
8883 readArchives(1);
8884
8885 printMsg("INFO", "creating library API dump ...");
8886 if(not keys(%{$MethodInfo{1}})) {
8887 printMsg("WARNING", "empty dump");
8888 }
8889
8890 my $MInfo = {};
8891 my $MNum = 0;
8892 foreach my $Method (sort keys(%{$MethodInfo{1}}))
8893 {
8894 my $MId = $MNum;
8895 if($REPRODUCIBLE) {
8896 $MId = getMd5($Method);
8897 }
8898 $MInfo->{$MId} = $MethodInfo{1}{$Method};
8899 $MInfo->{$MId}{"Name"} = $Method;
8900
8901 $MNum+=1;
8902 }
8903
8904 my $MUsed = {};
8905 $MNum = 0;
8906 foreach my $Inv (sort keys(%{$MethodUsed{1}}))
8907 {
8908 my $MId = $MNum;
8909 if($REPRODUCIBLE) {
8910 $MId = getMd5($Inv);
8911 }
8912
8913 $MUsed->{$MId}{"Name"} = $Inv;
8914 $MUsed->{$MId}{"Used"} = $MethodUsed{1}{$Inv};
8915
8916 $MNum+=1;
8917 }
8918
8919 my %API = (
8920 "MethodInfo" => $MInfo,
8921 "TypeInfo" => $TypeInfo{1},
8922 "MethodUsed" => $MUsed,
8923 # "FieldUsed" => $FieldUsed{1},
8924 "LibraryVersion" => $Descriptor{1}{"Version"},
8925 "LibraryName" => $TargetLibraryName,
8926 "Language" => "Java",
8927 "API_DUMP_VERSION" => $API_DUMP_VERSION,
8928 "JAPI_COMPLIANCE_CHECKER_VERSION" => $TOOL_VERSION
8929 );
8930
8931 my $DumpPath = "api_dumps/$TargetLibraryName/".$TargetLibraryName."_".$Descriptor{1}{"Version"}.".api.".$AR_EXT;
8932 if($OutputDumpPath)
8933 { # user defined path
8934 $DumpPath = $OutputDumpPath;
8935 }
8936
8937 my $Archive = ($DumpPath=~s/\Q.$AR_EXT\E\Z//g);
8938
8939 my ($DDir, $DName) = separate_path($DumpPath);
8940 my $DPath = $TMP_DIR."/".$DName;
8941 if(not $Archive) {
8942 $DPath = $DumpPath;
8943 }
8944
8945 mkpath($DDir);
8946
8947 open(DUMP, ">", $DPath) || die ("can't open file \'$DPath\': $!\n");
8948 print DUMP Dumper(\%API);
8949 close(DUMP);
8950
8951 if(not -s $DPath) {
8952 exitStatus("Error", "can't create API dump because something is going wrong with the Data::Dumper module");
8953 }
8954
8955 if($Archive) {
8956 $DumpPath = createArchive($DPath, $DDir);
8957 }
8958
8959 if($OutputDumpPath) {
8960 printMsg("INFO", "dump path: $OutputDumpPath");
8961 }
8962 else {
8963 printMsg("INFO", "dump path: $DumpPath");
8964 }
8965 exit(0);
8966 }
8967 if(not $Descriptor{1}{"Path"}) {
8968 exitStatus("Error", "-old option is not specified");
8969 }
8970 if(not -e $Descriptor{1}{"Path"}) {
8971 exitStatus("Access_Error", "can't access \'".$Descriptor{1}{"Path"}."\'");
8972 }
8973 if(not $Descriptor{2}{"Path"}) {
8974 exitStatus("Error", "-new option is not specified");
8975 }
8976 if(not -e $Descriptor{2}{"Path"}) {
8977 exitStatus("Access_Error", "can't access \'".$Descriptor{2}{"Path"}."\'");
8978 }
8979
8980 if($Quick)
8981 {
8982 $TypeProblems_Kind{"Binary"}{"Interface_Added_Super_Interface"} = "Low";
8983 $TypeProblems_Kind{"Binary"}{"Abstract_Class_Added_Super_Abstract_Class"} = "Low";
8984 $TypeProblems_Kind{"Binary"}{"Abstract_Class_Added_Super_Interface"} = "Low";
8985 $TypeProblems_Kind{"Binary"}{"Abstract_Class_Added_Abstract_Method"} = "Low";
8986 $TypeProblems_Kind{"Binary"}{"Interface_Added_Abstract_Method"} = "Low";
8987 }
8988
8989 detect_default_paths();
8990
8991 checkVersionNum(1, $Descriptor{1}{"Path"});
8992 checkVersionNum(2, $Descriptor{2}{"Path"});
8993
8994 createDescriptor(1, $Descriptor{1}{"Path"});
8995 createDescriptor(2, $Descriptor{2}{"Path"});
8996
8997 if(not $Descriptor{1}{"Archives"}) {
8998 exitStatus("Error", "descriptor d1 does not contain Java ARchives");
8999 }
9000 if(not $Descriptor{2}{"Archives"}) {
9001 exitStatus("Error", "descriptor d2 does not contain Java ARchives");
9002 }
9003 initLogging(1);
9004 initLogging(2);
9005
9006 if($Descriptor{1}{"Archives"}
9007 and not $Descriptor{1}{"Dump"}) {
9008 readArchives(1);
9009 }
9010 if($Descriptor{2}{"Archives"}
9011 and not $Descriptor{2}{"Dump"}) {
9012 readArchives(2);
9013 }
9014
9015 foreach my $Inv (keys(%{$MethodUsed{2}}))
9016 {
9017 foreach my $M (keys(%{$MethodUsed{2}{$Inv}}))
9018 {
9019 my $InvType = $MethodUsed{2}{$Inv}{$M};
9020
9021 if($InvType ne "static"
9022 and index($Inv, "<init>")==-1)
9023 {
9024 my $CName = $Inv;
9025 $CName=~s/\A\"\[L(.+);"/$1/g;
9026 $CName=~s/#/./g;
9027
9028 if($CName=~/\A(.+?)\./)
9029 {
9030 $CName = $1;
9031 if($CName!~/\"/)
9032 {
9033 $CName=~s!/!.!g;
9034 $ClassMethod_AddedUsed{$CName}{$Inv} = $M;
9035 }
9036 }
9037 }
9038
9039 if(not defined $MethodInfo{1}{$M}) {
9040 delete($MethodUsed{2}{$Inv}{$M});
9041 }
9042 }
9043 }
9044
9045 foreach my $ClassName (keys(%ClassMethod_AddedUsed))
9046 {
9047 foreach my $MethodName (keys(%{$ClassMethod_AddedUsed{$ClassName}}))
9048 {
9049 if(defined $MethodInfo{1}{$MethodName}
9050 or defined $MethodInfo{2}{$MethodName}
9051 or defined $MethodUsed{1}{$MethodName}
9052 or findMethod($MethodName, 2, $ClassName, 1))
9053 { # abstract method added by the new super-class (abstract) or super-interface
9054 delete($ClassMethod_AddedUsed{$ClassName}{$MethodName});
9055 }
9056 }
9057 if(not keys(%{$ClassMethod_AddedUsed{$ClassName}})) {
9058 delete($ClassMethod_AddedUsed{$ClassName});
9059 }
9060 }
9061
9062 prepareMethods(1);
9063 prepareMethods(2);
9064
9065 detectAdded();
9066 detectRemoved();
9067
9068 printMsg("INFO", "comparing classes ...");
9069 mergeClasses();
9070 mergeMethods();
9071
9072 foreach my $M (keys(%CompatProblems))
9073 {
9074 foreach my $K (keys(%{$CompatProblems{$M}}))
9075 {
9076 foreach my $L (keys(%{$CompatProblems{$M}{$K}}))
9077 {
9078 if(my $T = $CompatProblems{$M}{$K}{$L}{"Type_Name"}) {
9079 $TypeProblemsIndex{$T}{$M} = 1;
9080 }
9081 }
9082 }
9083 }
9084
9085 printReport();
9086
9087 if($RESULT{"Source"}{"Problems"} + $RESULT{"Binary"}{"Problems"}) {
9088 exit($ERROR_CODE{"Incompatible"});
9089 }
9090 else {
9091 exit($ERROR_CODE{"Compatible"});
9092 }
9093}
9094
9095scenario();
Note: See TracBrowser for help on using the repository browser.