./0000700000175000017500000000000011235353603011140 5ustar twernertwerner./clirr-0.6/0000755000175000017500000000000010316146510012562 5ustar twernertwerner./clirr-0.6/xdocs/0000755000175000017500000000000010316146510013702 5ustar twernertwerner./clirr-0.6/xdocs/index.xml0000644000175000017500000000526010316146510015536 0ustar twernertwerner Clirr Lars Khne

Clirr is a tool that checks Java libraries for binary and source compatibility with older releases. Basically you give it two sets of jar files and Clirr dumps out a list of changes in the public api. The Clirr Ant task can be configured to break the build if it detects incompatible api changes. In a continuous integration process Clirr can automatically prevent accidental introduction of binary or source compatibility problems.

To learn more about binary compatibility of Java APIs the follwing articles provide some detailed background material:

The name clirr is derived from the binary compatibility property that a new release of a library/component should have (cited from the above mentioned Eclipse article):

Pre-existing Client binaries must link and run with new releases of the Component without recompiling.

You can also use clirr as a verb, as in Last night the build clirred, meaning that binary incompatibility problems were detected and broke the build.

./clirr-0.6/xdocs/navigation.xml0000644000175000017500000000241010316146510016560 0ustar twernertwerner Clirr ./clirr-0.6/xdocs/contributing.xml0000644000175000017500000000576510316146510017150 0ustar twernertwerner Clirr Ant Task Lars Khne

If you have made an improvement to the Clirr source code, documentation, or anything else, please consider contributing it back to the community.

You can place patches or new code in the sourceforge patch tracker. Please send an announcement and explanations to the clirr developer mailing list.

Note that anything you contribute must have the same license as the rest of Clirr, i.e. LGPL, Copyright Lars Khne.

Licensing does matter and can get very confusing to maintain. Handing over copyrights to Lars keeps things simple for us to manage licensing issues. We do this for free and do not want to waste time on legal issues and managing who has copyright of which parts of the code. The only safe option is to have people hand over copyright to a person (like Lars) or an organisation (like Apache).

Some people feel that handing over copyright to Lars limits their rights in the code they contribute.

In fact it doesn't. Think about it: You give copyright to Lars, Lars immediately gives you the right to use the code under LGPL. What have you lost? You can still

Because the code is licensed under the LGPL, the only thing you give up by assigning copyright to Lars is the right to veto a re-licensing of the code. For example Lars could re-license Clirr under the Apache license without having to contact everybody who has ever contributed. Note that you would not lose any of the work you (and others) have done as it would still be licensed under LGPL - noone can ever take these rights away from you!

./clirr-0.6/xdocs/download.xml0000644000175000017500000000477310316146510016246 0ustar twernertwerner Download Clirr Lars Khne

The latest release of Clirr is available from the Sourceforge download page.

To compile Clirr yourself you need to have the following tools installed on your machine and available in the command line search path:

To build Clirr, open a command line shell, cd to an empty directory and enter the following commands (when prompted for a password for anonymous, simply press the Enter key):

Note that the anonymous CVS server on Sourceforge is under heavy load, and you might experience difficulties when checking out the sources. This is temporary, please try again a few minutes later. Sourceforge is currently in the process of installing more capable hardware.

./clirr-0.6/xdocs/images/0000755000175000017500000000000010316146510015147 5ustar twernertwerner./clirr-0.6/xdocs/images/clirr.png0000644000175000017500000000242510316146510016773 0ustar twernertwernerPNG  IHDRw87g3bKGDZ pHYs  ~tIME  4ҦqIDATx;R#16X8 {Ř#8Yjy*}U)_j4@h4Fh4F1%7np=?on7UwF{rc}߳딖 Uĥ +?<޿Oմߴ+,֯RCX㔛 j)jn+~u3e˭)#jsKMJ^;@IڜEn+l(HE#a}d, gEp)cEg1V®\Wg,K{7[%;l늣<7"#'oֱO>ysۭ >`2@v1K?y{f7nƾGMTqaw8dR`T.m8cGWS9ɋwj`$SI0X.ep(3~&+UܲdGz%Y qCA%KSz}Uea]f-י[Y/~(* X-yP1b"~.NO2e/&]YaskERQa1K@eWL'A}Ӛإu,WN:Ľ:)8|Z ^YC*XZ5q9e,#{UHU[m jporpl䄨 K j^;C2^4x ˒STfZYNr^~*+閯N"e󄐠0!L-D+`>'Am%F K+b"@h5Nܘp;$p^h RC*{MuI_: ]R-ٖƘe7(W? ) D˽v8]\$[@NE,sq5mAX6/0{[.w8|d 3"% ~2kD֛H:IX'ާ)h3f5]_5Fh4Fј+VIENDB`./clirr-0.6/xdocs/related.xml0000644000175000017500000000303510316146510016045 0ustar twernertwerner
  • JDiff is a javadoc plugin that reports api contract changes, based on the Javadoc of the code. This approach is complementary to that of Clirr - JDiff seems to be a great tool for manual inspection during release preparation, while Clirr is well suited to detect one class of compatibility problems automatically during the nightly build.
  • The APIComparatorPlugin for the IntelliJ IDEA IDE is an interactive tool that presents binary api diffs visually.
  • If you like Clirr because you want to deliver quality work to your users, you might want to have a look at Checkstyle, a tool for automatic code inspection. The development teams of Clirr and Checkstyle overlap, so if you like Clirr, you will probably love Checkstyle.
./clirr-0.6/maven.xml0000644000175000017500000000521310316146510014413 0ustar twernertwerner ./clirr-0.6/project.properties0000644000175000017500000000065310316146510016352 0ustar twernertwernermaven.xdoc.date=left maven.xdoc.version=${pom.currentVersion} maven.checkstyle.properties=${basedir}/conf/clirr_checks.xml maven.checkstyle.header.file=${basedir}/conf/javaheader.txt maven.checkstyle.fail.on.violation=true # hide irrelevant stuff from the nav bar maven.xdoc.poweredby.image= maven.xdoc.developmentProcessUrl= # place subproject's sites into root context of aggregating site maven.multiproject.aggregateDir= ./clirr-0.6/maven/0000755000175000017500000000000010316146510013670 5ustar twernertwerner./clirr-0.6/maven/xdocs/0000755000175000017500000000000010316146510015010 5ustar twernertwerner./clirr-0.6/maven/xdocs/index.xml0000644000175000017500000000155010316146510016642 0ustar twernertwerner Maven plugin for Clirr Vincent Massol

The Maven plugin for Clirr allows easily running the Clirr framework on Maven projects. It supports the following features:

  • Execution of Clirr framework on the current project.
  • Automatic download of project baseline jars with which the current development source are compared with.
  • Automatically try to guess the latest release version of the current project by looking at the POM's <version> tag.
./clirr-0.6/maven/xdocs/changes.xml0000644000175000017500000000144610316146510017147 0ustar twernertwerner Plugin changes Vincent Massol Upgraded to version 0.6 of clirr-core. The new apiclasses feature in the Ant task is currently not supported by the maven plugin. Upgraded to version 0.5 of clirr-core. Initial release. See the plugin's web page for the features list. ./clirr-0.6/maven/xdocs/navigation.xml0000644000175000017500000000170610316146510017675 0ustar twernertwerner Clirr Maven plugin
Hosted by sourceforge
./clirr-0.6/maven/xdocs/installing.xml0000644000175000017500000000241610316146510017701 0ustar twernertwerner Installing the Maven Clirr plugin Vincent Massol

There are 2 options to install the Clirr plugin in an existing Maven installation:

  • Automatic install: Call maven plugin:download -DgroupId=clirr -DartifactId=clirr-maven -Dversion={version} where {version} is the version to use. Maven will automatically download the plugin and install it, uninstalling any other version you may already have.
  • Manual install: Download the Clirr plugin jar and copy it in {MAVEN_HOME}/plugins where {MAVEN_HOME} is the location where you have installed Maven. Make sure you also remove any previous Clirr plugin from {MAVEN_HOME}/plugins and delete your {USER_HOME}/.maven/plugins directory.
./clirr-0.6/maven/xdocs/using.xml0000644000175000017500000000211310316146510016654 0ustar twernertwerner Using the Maven Clirr plugin Vincent Massol
  1. Make sure you have the Clirr plugin installed.
  2. Make sure you have the old versions of your project declared in your POM. The versions should be declared from oldest to youngest. Clirr will check the current code against the latest released version, ingnoring the current development version (version control tag 'HEAD').
  3. Type "maven clirr". This will create an XML file that contains all changes in your public API. For each change, the file includes Clirr's compatibility analysis.
./clirr-0.6/maven/xdocs/goals.xml0000644000175000017500000000101610316146510016635 0ustar twernertwerner Maven Clirr plugin goals Vincent Massol clirr Default goal (does the same as clirr:check). clirr:check Run compatibility checks. ./clirr-0.6/maven/xdocs/properties.xml0000644000175000017500000000304510316146510017730 0ustar twernertwerner Maven Clirr properties Vincent Massol
Property Optional? Description
clirr.baseline.download yes Decide whether the plugin will try to download the baseline jar or not. Default is true
clirr.fail.on.warning yes Decide whether to fail the build on Clirr-detected warnings. Default is false.
clirr.fail.on.error yes Decide whether to fail the build on Clirr-detected errors. Default is true.
./clirr-0.6/maven/maven.xml0000644000175000017500000000024310316146510015517 0ustar twernertwerner ./clirr-0.6/maven/plugin.jelly0000644000175000017500000001126110316146510016230 0ustar twernertwerner No released version found in the POM. Please specify a baseline version using the clirr.baseline.version property. Cannot find file [${clirr.baseline.destination}] ./clirr-0.6/maven/src/0000755000175000017500000000000010316146510014457 5ustar twernertwerner./clirr-0.6/maven/src/test/0000755000175000017500000000000010316146510015436 5ustar twernertwerner./clirr-0.6/maven/src/test/net/0000755000175000017500000000000010316146510016224 5ustar twernertwerner./clirr-0.6/maven/src/test/net/sf/0000755000175000017500000000000010316146510016634 5ustar twernertwerner./clirr-0.6/maven/src/test/net/sf/clirr/0000755000175000017500000000000010316146510017747 5ustar twernertwerner./clirr-0.6/maven/src/test/net/sf/clirr/maven/0000755000175000017500000000000010316146510021055 5ustar twernertwerner./clirr-0.6/maven/src/test/net/sf/clirr/maven/ClirrUtilsTest.java0000644000175000017500000000461110316146510024656 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// //Clirr: compares two versions of a java library for binary compatibility //Copyright (C) 2004 Lars Khne // //This library is free software; you can redistribute it and/or //modify it under the terms of the GNU Lesser General Public //License as published by the Free Software Foundation; either //version 2.1 of the License, or (at your option) any later version. // //This library is distributed in the hope that it will be useful, //but WITHOUT ANY WARRANTY; without even the implied warranty of //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //Lesser General Public License for more details. // //You should have received a copy of the GNU Lesser General Public //License along with this library; if not, write to the Free Software //Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.maven; import java.util.ArrayList; import org.apache.maven.project.Version; import junit.framework.TestCase; /** * Unit tests for {@link ClirrUtils}. * * @author Vincent Massol */ public class ClirrUtilsTest extends TestCase { public void testGetLatestVersionWhenNoVersionElementsDefined() { String result = ClirrUtils.getLatestVersion(new ArrayList()); assertNull(result); } public void testGetLatestVersionWhenNoReleasedVersionElementDefined() { Version unreleasedVersion = new Version(); unreleasedVersion.setId("0.1"); unreleasedVersion.setTag("HEAD"); ArrayList versions = new ArrayList(); versions.add(unreleasedVersion); String result = ClirrUtils.getLatestVersion(versions); assertNull(result); } public void testGetLatestVersionWhenReleasedVersionElementsDefined() { Version releasedVersion = new Version(); releasedVersion.setId("0.1"); releasedVersion.setTag("RELEASE_DUMMY_0_1"); Version unreleasedVersion = new Version(); unreleasedVersion.setId("0.2"); unreleasedVersion.setTag("HEAD"); ArrayList versions = new ArrayList(); versions.add(releasedVersion); versions.add(unreleasedVersion); String result = ClirrUtils.getLatestVersion(versions); assertEquals("0.1", result); } } ./clirr-0.6/maven/src/main/0000755000175000017500000000000010316146510015403 5ustar twernertwerner./clirr-0.6/maven/src/main/net/0000755000175000017500000000000010316146510016171 5ustar twernertwerner./clirr-0.6/maven/src/main/net/sf/0000755000175000017500000000000010316146510016601 5ustar twernertwerner./clirr-0.6/maven/src/main/net/sf/clirr/0000755000175000017500000000000010316146510017714 5ustar twernertwerner./clirr-0.6/maven/src/main/net/sf/clirr/maven/0000755000175000017500000000000010316146510021022 5ustar twernertwerner./clirr-0.6/maven/src/main/net/sf/clirr/maven/ClirrUtils.java0000644000175000017500000000557110316146510023771 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// //Clirr: compares two versions of a java library for binary compatibility //Copyright (C) 2004 Lars Khne // //This library is free software; you can redistribute it and/or //modify it under the terms of the GNU Lesser General Public //License as published by the Free Software Foundation; either //version 2.1 of the License, or (at your option) any later version. // //This library is distributed in the hope that it will be useful, //but WITHOUT ANY WARRANTY; without even the implied warranty of //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //Lesser General Public License for more details. // //You should have received a copy of the GNU Lesser General Public //License along with this library; if not, write to the Free Software //Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.maven; import java.io.File; import java.util.List; import org.apache.maven.jelly.MavenJellyContext; import org.apache.maven.project.Version; import org.apache.maven.util.HttpUtils; /** * Utility class to manipulate POM version information. * * @author Vincent Massol */ public class ClirrUtils { /** * @return the latest released version, which means the latest version * listed in the POM <version> elements with a * tag different from HEAD. Returns * null if no latest released version is found * @param versions the list of {@link Version} objects from the POM */ public static String getLatestVersion(List versions) { String result = null; if (!versions.isEmpty()) { int pos = versions.size(); while (pos > 0) { Version latestVersion = (Version) versions.get(pos - 1); // Is it a released version? if (!latestVersion.getTag().equalsIgnoreCase("HEAD")) { result = latestVersion.getId(); break; } else { pos = pos - 1; } } } return result; } /** * TODO: Add support for proxies */ public static void getBaselineJar(MavenJellyContext context) throws Exception { String targetFileName = (String) context.getVariable("clirr.baseline.destination"); File targetFile = new File(targetFileName); HttpUtils.getFile( (String) context.getVariable("clirr.baseline.url"), targetFile, false, true, null, null, null, null); } } ./clirr-0.6/maven/plugin.properties0000644000175000017500000000277110316146510017313 0ustar twernertwerner# ------------------------------------------------------------------- # Default properties for the Clirr Plugin # ------------------------------------------------------------------- # Decide whether the plugin will try to download the baseline jar or not clirr.baseline.download = true # (optional) Baseline version to check binary compatibility with. # If the clirr.compatibility.version property is not defined, we get # the last released version from the POM using the tag. We # assume the latest version is the last one in the list. # # Example: # clirr.baseline.version = 0.3 # (optional) By default the plugin tries to guess the value of # clirr.baseline.destination. However you can override this property if you # wish to point to a jar located in a specific location you wish to use # rather than the default which is in the local repository. # # Note If you specify this property, the plugin will not try to download the # baseline jar # # Ex: clirr.baseline.destination = /tmp/myjar.jar # Type of the project artifact to verify clirr.baseline.type = jar # Location where the project artifacts can be found. clirr.baseline.url = http://www.ibiblio.org/maven/${pom.groupId}/${clirr.baseline.type}s # Decide whether to fail the build on Clirr-detected warnings. clirr.fail.on.warning = false # Decide whether to fail the build on Clirr-detected errors. clirr.fail.on.error = true # Name and location of the XML Clirr-generated report file clirr.report.file = ${maven.build.dir}/clirr.xml ./clirr-0.6/maven/project.xml0000644000175000017500000000503210316146510016060 0ustar twernertwerner ${basedir}/../project.xml clirr-maven Clirr Maven plugin 0.6 net.sf.clirr.maven Maven plugin for Clirr Maven plugin for Clirr /home/groups/c/cl/clirr/htdocs/maven scm:cvs:pserver:anonymous@cvs.sourceforge.net:/cvsroot/clirr/maven:clirr http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/clirr/maven net.sf.clirr clirr-core ${pom.currentVersion} bcel bcel 5.1 http://jakarta.apache.org/bcel maven maven 1.0.2 commons-jelly commons-jelly 20030902.160215 commons-lang commons-lang 2.0 src/main src/plugin-resources plugin-resources . plugin.jelly plugin.properties project.properties project.xml maven-changes-plugin maven-changelog-plugin maven-file-activity-plugin maven-developer-activity-plugin maven-file-activity-plugin maven-license-plugin maven-linkcheck-plugin ./clirr-0.6/core/0000755000175000017500000000000010316146510013512 5ustar twernertwerner./clirr-0.6/core/xdocs/0000755000175000017500000000000010316146510014632 5ustar twernertwerner./clirr-0.6/core/xdocs/index.xml0000644000175000017500000000526010316146510016466 0ustar twernertwerner Clirr Lars Khne

Clirr is a tool that checks Java libraries for binary and source compatibility with older releases. Basically you give it two sets of jar files and Clirr dumps out a list of changes in the public api. The Clirr Ant task can be configured to break the build if it detects incompatible api changes. In a continuous integration process Clirr can automatically prevent accidental introduction of binary or source compatibility problems.

To learn more about binary compatibility of Java APIs the follwing articles provide some detailed background material:

  • Report all API changes (currently only partially implemented)
  • Evaluate each change wrt. binary and source compatibility
  • support plain text and XML reports
  • Flexible failure handling (warnings vs. errors, break the build or set error property)

The name clirr is derived from the binary compatibility property that a new release of a library/component should have (cited from the above mentioned Eclipse article):

Pre-existing Client binaries must link and run with new releases of the Component without recompiling.

You can also use clirr as a verb, as in Last night the build clirred, meaning that binary incompatibility problems were detected and broke the build.

./clirr-0.6/core/xdocs/anttask.xml0000644000175000017500000001754110316146510017031 0ustar twernertwerner Clirr Ant Task Lars Khne

The clirr-core module contains an Ant task. The following text assumes that you are familiar with Ant. To run clirr, you typically

  • compile the current version of the library you want to check, resulting in one or more jar file
  • tell ant about the clirr ant task
  • download the compatibility baseline release of your software from a central location via http (if it is not available in your local filesystem already)
  • run the clirr task

To do this you will need an ant snippet similar to the following:

<target name="checkbinarycompatibility" depends="build"> <!-- buildtools.classpath should either contain clirr-core-uber.jar or alternatively clirr-core.jar and the libraries it depends on --> <taskdef classpathref="buildtools.classpath" resource="clirrtask.properties"/> <property name="jar.baseline" value="${ant.project.name}-${compatibility.baseline.version}.jar"/> <get src="${url.libbase}/${ant.project.name}/${jar.baseline}" dest="build/tmp/${jar.baseline}"/> <clirr> <origfiles dir="build/tmp" includes="${jar.baseline}"/> <newfiles dir="build/lib" includes="${jar.buildresult}"/> <!-- <formatter type="xml" outfile="build/clirr.xml" /> --> <!-- TODO: example for 3rd party classpath --> </clirr> </target>
Attribute Description Required Default
failOnBinWarning Whether task execution should fail (break the build) on warnings about binary compatibility issues No No
failOnBinError Whether task execution should fail (break the build) on binary compatibility errors No Yes
failOnSrcWarning Whether task execution should fail (break the build) on warnings about source code compatibility issues No No
failOnSrcError Whether task execution should fail (break the build) on source compatibility errors No Yes

A FileSet that describes the current version that should be checked against the compatibility baseline.

Clirr works with FileSets instead of individual jar files to allow splitting up or combining library distributions. An example is log4j, presumably the 1.3.0 release will split up the earlier log4j.jar into several jar files.

A FileSet that describes the compatibility baseline to check against.

The 3rd party ClassPath that is referenced by the checked library version (newFiles). Any class or interface that occurs as a baseclass, parameter type or method return type must be included here.

The 3rd party ClassPath that is referenced by the compatibility baseline version (origFiles). Any class or interface that occurs as a baseclass, parameter type or method return type must be included here.

Often the origClassPath is the same as the newClassPath. In these cases you can specify these paths using the refid attribute to avoid duplicating the classpath information. Please refer to the ant manual for details.

A formatter that generates Clirr output. Multiple formatters can be specified. Available attributes for each formatter element:

Attribute Description Required Default
type The formatter type. Available types are plain and xml No plain
outfile The file to write to. If not specified, output is written to stdout No stdout

If no formatter is specified, Clirr will write it's findings to stdout in plain format.

A PatternSet that defines which classes form the API. By default all classes are included.

The API is often only a subset from the set of public classes in a jar file. For example, the Eclipse project has package naming conventions that signal which classes must not be used outside a module, even though they are technically public.

Example:

<clirr> <origfiles dir="build/tmp" includes="${jar.baseline}"/> <newfiles dir="build/lib" includes="${jar.buildresult}"/> <apiclasses> <exclude name="**/internal/**"/> <exclude name="**/examples/**"/> <exclude name="**/tests/**"/> </apiclasses> </clirr>
./clirr-0.6/core/xdocs/changes.xml0000644000175000017500000001563410316146510016775 0ustar twernertwerner Changes Lars Khne Clirr now provides a SPI to represent the java code structure, the checks now work against this SPI. Clirr provides a SPI implementation that works on jar files, but other implementations can be used as well. For example, an IDE will typically already have an in-memory representation of the Java sources, so a Clirr IDE plugin could use that (via an adapter) and would not require a full compile before running Clirr. The API will most probably be in flux when Clirr adds support for Java 1.5 features like generics and varargs. Ant task: Class exclusion via apiclasses subelement was broken on Windows. CLI: Handling of multi-jar libraries in parameters -o and -n was broken on Windows. CLI: Allow specification of 3rd party jars via new options -ocp and -ncp. Improved test coverage. Improved startup time of commandline uberjar. Ant task now works with uberjar. Avoid false alarms when package visible classes are removed. Removed the ant.jar from the uberjar file, resulting in a much smaller distribution size. The Ant task now allows to exclude classes from compatibility checks via the 'apiclasses' subelement. Detect change of method final modifier. Improved change messages if field accessibility is weakened/strengthened. Detect 'pull up in class hierarchy' refactoring for methods. XML formatter did not write method and field attributes correctly. Report on methods being deprecated or undeprecated. Added a command-line interface, net.sf.clirr.cli.Clirr, for running checks and generating reports from the command-line. Removed abstract methods that are specified by an implemented interface are no longer reported as a compatibility problem. Report on classes changing accessibility (top-level classes changing between public and package, or nested classes changing between any of public/protected/package/private). It is no longer an error to add a "final" attribute to a class which has no public or protected constructors, as it was always impossible to derive subclasses from it anyway. Clirr now analyses code changes for source code compatibility problems as well. Note: Ant task attribute names and the output format of the XML formatter have changed to support this feature. Error messages are now localized. Initial supported languages are english and german. Treat all changes in package and private items as being of INFO level severity only, ie changes which would result in an ERROR being reported if they were public or protected are reported with a severity of INFO if they are package or private. Fixed a copy + paste error in field modifier comparison logic that would lead to false alarms and undetected compatibility problems. Detect changes of field types. Ant Task fails when filesets origFiles or newFiles are empty. Empty file sets are usually a setup problem - they should not create the impression that there are no compatibility problems, just because Clirr didn't report anything. Documented formatter subelements in Ant task. Warn about compile time constant value changes. Changing the value of a constant is not binary incompatible (you won't get any Exception), but client code compiled against the old version of the library will have the old value inlined and continue to use that old value. See the Java Language Spec, Chapter 13.4.8, for details. Warn about adding new superclasses to a class derived from java.lang.Throwable. Such changes are not binary incompatible (you won't get any Exception), but a different catch clause might get selected in client code. Initial public release. ./clirr-0.6/core/xdocs/runcli.xml0000644000175000017500000000126310316146510016652 0ustar twernertwerner Clirr Command-Line Usage Lars Khne

Clirr provides an "uberjar", which bundles all the necessary code, including all required libraries, into a single executable jar file. Clirr can then be run from a commandline via the command: java -jar clirr-core-@VERSION@-uber.jar Running clirr with no command-line arguments will display the available options.

./clirr-0.6/core/xdocs/exegesis.xml0000644000175000017500000007645210316146510017206 0ustar twernertwerner Difference Message Reference Manual The Clirr Development Team

When clirr generates an ERROR, WARNING or INFO message about a change in the jars being compared, there is an associated message reference code. This document contains an explanation of the meaning of that message which may contain information which could not be fitted into the brief message summary.

Messages are separated into three severity levels:

  • ERROR
  • WARNING
  • INFO

Errors come in two flavours:

  • Link-time failures, where an exception will be thrown as soon as code compiled against an old version of a class and the new version of the class are loaded into the same classloader hierarchy.
  • Run-time failures, where an exception is thrown when code compiled against the old version of a class attempts to call a method on a new version of the class, or vice versa.

Clirr reports "errors" for cases where it is possible to get a run-time failure. Whether one actually occurs can depend upon the way the library is called, ie changes reported as an error may in fact work when used as long as the patterns of use of the library do not trigger the failure situation.

Warnings are issued for situations where no link or runtime exception will occur, but where the application may behave unexpectedly due to the changes that have occurred.

Information messages provide users with information about new features which have been added without breaking backward compatibility in any way.

When using clirr to report on changes to items which have private or package scope, these changes are always reported as INFO level changes, never WARNING or ERROR level. This allows users of clirr to generate "change reports" at a level suitable for developers without having some of those changes marked (irrelevantly) as binary incompatibilities.

There can never be binary incompatibilities for changes to private classes, methods or fields as that access can only occur from within the same class (ie the same compilation unit).

Clirr does not report binary incompatibility WARNINGs or ERRORs for package-scoped items either, because java packages are intended to be "release units", ie all classes within a package are compiled together (ensuring compatibility) then released as a unit. The only time that package-scope incompatibilities could possibly be an issue is when users of a library write their own classes using a package declaration belonging to some external library, or when a subset of updated classes (eg a single class) from a package is used to override certain classes from a previous release of the library. Both of these situations are considered very poor practice by java programming convention.

In the following sections, the term "old" is used to refer to a class, interface, method or field from the set of jars which represent the old/previous/original/baseline version of the library being inspected. The term "new" is used to refer to a class, interface, method or field from the set of jars which represent the new/current/latest version of the library being inspected.

In the following sections, the term "type" is used to refer to something which may be either a class or interface.

Severity: INFO

The specified type exists in both versions, but its declared access specifier has changed to relax restrictions on what other code can access it.

Top-level types (ie those which are not nested within another class) may only have "package" or "public" accessibility. Nested types can take on any of the four available accessibility values.

Regardless of whether the object is top-level or nested, a change in accessibility from left-to-right of the sequence private->package->protected->public always ensures that all code which could previously access that type can still access that type. Therefore such a change is always binary and source-code compatible.

Note that the declaration "protected" provides access to both code derived from the type and to code within the same package, ie "protected" accessibility also implies package accessibility.

Severity: ERROR

The specified type exists in both versions, but its declared access specifier has changed to tighten the restrictions on what other code may access it.

Top-level types (ie those which are not nested within another class) may only have "package" or "public" accessibility. Nested types can take on any of the four available accessibility values.

Regardless of whether the type is top-level or nested, a change in accessibility from left-to-right of the sequence public->protected->package->private may cause existing code which could previously access the type to no longer be able to do so.

Section 13.4.3 of the java language specification states explicitly that an IllegalAccessError should occur if a pre-existing binary tries to access a type when the type accessibility has been changed to something that would cause a compile-time error. However this does not appear to be enforced in practice, at least in current JVMs. Nevertheless this should be an error, and so clirr reports this change as a binary-compatibility ERROR.

Severity: ERROR

The specified class has become an interface in the new version. This change is always a binary and source-code incompatibility, for obvious reasons.

Severity: ERROR

The specified interface has become an class in the new version. This change is always a binary and source-code incompatibility, for obvious reasons.

Severity: INFO

The specified class was declared final in the old version, but is no longer final in the new version.

Severity: INFO

The specified class was not declared final in the old version, but is now declared final. Normally, this would be an incompatibility because pre-existing derived classes would no longer be valid when used with the new version of this class. However in this case the old class version had no public or protected constructors, so it was not possible for any derived classes to exist even for the old version of the library. Changing such a class to final therefore can not break any existing code.

Severity: ERROR

The specified class was not declared final in the old version, but is now declared final. Any pre-existing classes which were declared as subclasses of this class will therefore not be valid with the new version of the library.

A VerifyError is thrown by the classloader when an attempt is made to load a subclass of a final class.

Note that a class Y is loaded by the standard classloader only when the first attempt is made to create an instance of Y, or to directly reference the Class object for class Y. If some other class X has class Y as a declared member, or as a parameter to some method, then loading class X does not cause class Y to be loaded.

Severity: INFO

The old version of this class was declared to be an abstract class. The new version is not abstract, allowing users to create instances of the class.

Severity: ERROR

The old version of this class was not declared to be abstract. The new version is abstract. Pre-existing code which creates instances of this class is no longer valid with the new version.

Severity: INFO

The new version of the type now implements an additional interface. This does not invalidate any existing code (source or binary), and is a completely backward-compatible change.

Note that this message can be reported without any change occurring in the specified type; a change to the set of interfaces supported by a type will cause this message to be reported for every descendant of that type.

Severity: ERROR

The old version of this type declared that it implemented an interface which the new class or interface does not. Existing code which explicitly or implicitly casts objects of this type to the now missing interface is no longer valid.

Note that this message can be reported without any change occurring in the specified type; a change to the set of interfaces supported by a type will cause this message to be reported for every descendant of that type.

Severity: INFO or WARNING

The new version of the class has a class in its inheritance hierarchy which the old version did not, either because its direct parent is now a different class, or because one of its parent classes has changed its inheritance hierarchy.

If the specified class has java.lang.Throwable as an ancestor, then this change is reported as a WARNING, because this class change may change the exception-catching behaviour of programs that use this class.

Note that this message can be reported without any change occurring in the specified class; a change to the set of superclasses of an ancestor class will cause this message to be reported for every descendant class.

Severity: ERROR

The old version of this class has a class in its inheritance hierarchy which the new version does not, either because its direct parent is now a different class, or because one of its parent classes has changed its inheritance hierarchy.

Existing code which explicitly or implicitly casts objects of this type to the now missing class type is no longer valid.

Note that this message can be reported without any change occurring in the specified class; a change to the set of superclasses of an ancestor class will cause this message to be reported for every descendent class.

Note also that if this class has Throwable in its ancestry, then the class hierarchy change can also cause changes in the exception-catching behaviour of programs using this class.

Severity: INFO

The new class has an additional static or instance member. This change is completely backwards-compatible.

Severity: ERROR

The new class has removed a field present in the old version. Pre-existing code which directly accesses that field will no longer be valid.

Severity: WARNING

Code compiled against the old version of the class was permitted to "inline" the value of this field because it was a compile-time constant. Therefore, existing binary code will continue to use the old value of this field, instead of the new value (which cannot be inlined).

Severity: WARNING

Code compiled against the old version of the class was permitted to "inline" the value of this field because it was a compile-time constant. Therefore, existing binary code will continue to use the old value of this field, instead of the new value.

Severity: ERROR

The type associated with the specified static or instance member of the specified class has changed. Pre-existing code which directly accesses that field may no longer be valid, and therefore this is an incompatible change.

Severity: INFO

The field was previously final, and is no longer final. This means that the field value can now be modified during the lifetime of the class or instance.

Whether a value in a field could previously be "inlined" into other classes is an issue addressed by messages 6002 and 6003, not this message.

Severity: ERROR

The field can no longer be modified during the lifetime of the class or instance. Code which previously modified this field is therefore no longer valid.

Severity: ERROR

The field is now an instance variable rather than a class variable. Code which previously accessed this field via the Class rather than an instance of the class is no longer valid.

Severity: ERROR

The field is now a class variable rather than an instance variable.

For some reason (presumably internal implementation issues), the java standard declares that this change is not binary-compatible, and that an IncompatibleClassChangeError will be thrown if code compiled against the "old" version of a class is used together with a "new" version for which a field is now static.

Because source code is permitted to access class variables via instances of that class, this is expected to be a source-code compatible change. However currently CLIRR reports this as an ERROR for source-code compatibility too.

Severity: INFO

In the new version, the specified field is accessible to more code than it was previously.

Severity: ERROR

In the new version, the specified field is accessible to less code than it was previously. Therefore existing code may no longer be valid.

Binary Severity: WARNING

Source Severity: ERROR

The new class has removed a field present in the old version. Pre-existing source code which directly accesses that field will no longer be valid.

Previously, however, the field was final and was initialised with a constant value. Therefore code compiled against the previous version of the class will have inlined this constant and will continue to work, using the previous value of this field. A warning is issued as this is often not desirable behaviour. However it is not a binary incompatibility.

Severity: INFO

The old class had a method named X. The new class no longer has this method, but a parent class does define this method, so no binary or source incompatibility has occurred.

Note that this change may have the effect of forcing the new class to become 'abstract'. If this is the case, then this change is reported separately.

Severity: INFO

The old class or interface previously had a method named X. The new class or interface no longer has this method, but a parent interface does define this method, so no binary or source incompatibility has occurred.

Note that this change may have the effect of forcing the new class to become 'abstract'. If this is the case, then this change is reported separately.

Severity: ERROR

The old class or interface had a method named X. The new class or interface no longer has this method, and this method is not defined on any parent class or interface.

Whether an error actually occurs at runtime for this change depends on usage patterns. The modified class can be used with existing code as long as that existing code does not attempt to call the removed method. If a call to the missing method is made, then a NoSuchMethodError exception is generated when the method invocation occurs.

Severity: INFO

The specified method on the old class or interface was overriding an inherited definition. The new class or interface no longer has this method explicitly declared on it, but it still inherits a definition so there is no binary incompatibility.

Severity: ERROR

The specified method has had arguments added or removed. This means that code which previously invoked it will no longer invoke the same method.

If there is an inherited method definition with the old prototype, then there is no binary incompatibility; code which was compiled against the old version of this class will now invoke the inherited implementation. In this situation, clirr should output an INFO message rather than an error. However at the current date, clirr does not check for this situation.

If there is no inherited method definition with the old prototype, then the change is a binary incompatibility.

Binary Severity: INFO or ERROR

Source Severity: ERROR

The specified method has had the type of one or more of its arguments modified. This means that code compiled against the old version of the class will no longer invoke the same method. However exactly the same old source code, when compiled against the new class version may invoke this method if the argument types are assignment-compatible.

If there is an inherited method definition with the old prototype, then there is no binary incompatibility; code which was compiled against the old version of this class will now invoke the inherited implementation. At the current date, clirr does not check for this situation.

If there is no inherited method definition with the old prototype, then the change is a binary incompatibility.

If the parameter types changed were all changed to supertypes of their previous declared types, or for primitive parameter types if they were changed to "larger" types in every case, then the new code is source-code-compatible with the previous release even if it is not binary-compatible. Note that in this situation, recompiling code which uses the library may change its behaviour from calling an inherited method to calling a method on the class which has a slightly different prototype. At the current date, clirr does not check for this situation.

Binary Severity: ERROR

Source Severity: INFO or ERROR

The specified method has had its declared return type changed. Whether a problem actually occurs at runtime when using code compiled against the old version of this library depends upon usage patterns. Old code may call other methods on this class. However any attempt to call the method whose return type has changed will result in a NoSuchMethodError being thrown when the method is invoked, because the return type is part of the "method signature".

The change is source-code-compatible if and only if the new return type is assignable to the old return type. This means that:

  • if the old return type was a primitive type, then the new return type must be narrower than the old type.
  • if the old return type was an interface, then the new return type must be a class or interface which implements the old return type.
  • if the old return type was a class, then the new return type must be a subclass of the previously returned type.
Clirr does not currently check for source-code compatibility for changes in method return types; currently these are simply reported as an ERROR.

Severity: INFO

The specified method has been declared as "deprecated". This is always a binary-compatible change as well as a source-code-compatible change.

Severity: INFO

The specified method was declared "deprecated" in the previous version, but is no longer deprecated in the current release. While slightly unusual, this is permitted. This change is always a binary-compatible change as well as a source-code-compatible change.

Severity: ERROR

The access permissions associated with the specified method have been tightened to permit less user code to access the method.

Whether this change is a source-code compatibility issue or not depends upon patterns of usage.

This change should be a binary incompatibility. Note, however, that current JVMs do not validate this. Code compiled against a previous version of a class can successfully invoke methods for which they no longer have access rights. Nevertheless, the java language specification states that this is an error, so clirr reports this change as a binary incompatibility.

Severity: INFO

The access permissions associated with the specified method have been loosened to permit more user code to access the method. This is always a binary and source-code compatible change.

Severity: INFO

A non-abstract method has been added to the specified class. This is always a binary-compatible change.

It is also a source-code compatible change.

Q: if the new method overrides an inherited one, then which version does code compiled against the old library invoke?

Binary Severity: ERROR

Source Severity: ERROR

A method declaration has been added to the specified interface. This is always reported as a binary-compatibility error, but in practice the changed class might be used successfully with code compiled against the old interface depending upon usage patterns.

Old code which invokes methods upon code compiled against the new (expanded) interface will continue to work without issues. And old code which implements the old version of the interface will also continue to work correctly as long as no code attempts to invoke any of the newly-added methods against that instance. But code which (validly) invokes one of the new methods in the interface against an object which implements only the old version of the interface will cause an AbstractMethodError to be thrown at the time the method invocation is attempted.

Adding a method to an interface is always reported as an ERROR, because classes that implement that interface must now be modified to implement the declared method.

Binary Severity: ERROR

Source Severity: ERROR

An abstract method declaration has been added to the specified class. This is always reported as a binary-compatibility error, but in practice the changed class might be used successfully with code compiled against the old class depending upon usage patterns.

If instances of objects compiled against the old class are created, then their methods can be invoked without problems. But if the newly-added abstract method is ever invoked, then an AbstractMethodError is thrown at the time the method invocation is attempted.

Severity: ERROR

The method was previously non-final, and is now final. Subclasses of this class will no longer compile or run.

When the old class containig this method was final (explicitly or by only providing private constructors) then subclasses cannot exist. Clirr currently does not check for this situation, so this will raise a false alarm in some corner cases.

Severity: INFO

The method was previously final, and is now non-final. This is always a binary-compatible change.

Severity: INFO

The new version of the library has a class which was not present in the old version.

Severity: ERROR

The new version of the library no longer contains the specified class.

./clirr-0.6/core/xdocs/navigation.xml0000644000175000017500000000204510316146510017514 0ustar twernertwerner Clirr Core
Hosted by sourceforge
./clirr-0.6/core/xdocs/images/0000755000175000017500000000000010316146510016077 5ustar twernertwerner./clirr-0.6/core/xdocs/images/clirr.png0000644000175000017500000000242510316146510017723 0ustar twernertwernerPNG  IHDRw87g3bKGDZ pHYs  ~tIME  4ҦqIDATx;R#16X8 {Ř#8Yjy*}U)_j4@h4Fh4F1%7np=?on7UwF{rc}߳딖 Uĥ +?<޿Oմߴ+,֯RCX㔛 j)jn+~u3e˭)#jsKMJ^;@IڜEn+l(HE#a}d, gEp)cEg1V®\Wg,K{7[%;l늣<7"#'oֱO>ysۭ >`2@v1K?y{f7nƾGMTqaw8dR`T.m8cGWS9ɋwj`$SI0X.ep(3~&+UܲdGz%Y qCA%KSz}Uea]f-י[Y/~(* X-yP1b"~.NO2e/&]YaskERQa1K@eWL'A}Ӛإu,WN:Ľ:)8|Z ^YC*XZ5q9e,#{UHU[m jporpl䄨 K j^;C2^4x ˒STfZYNr^~*+閯N"e󄐠0!L-D+`>'Am%F K+b"@h5Nܘp;$p^h RC*{MuI_: ]R-ٖƘe7(W? ) D˽v8]\$[@NE,sq5mAX6/0{[.w8|d 3"% ~2kD֛H:IX'ާ)h3f5]_5Fh4Fј+VIENDB`./clirr-0.6/core/maven.xml0000644000175000017500000000160510316146510015344 0ustar twernertwerner ${clirr.testlibs} Compiling test input ${testlib} ./clirr-0.6/core/project.properties0000644000175000017500000000204610316146510017300 0ustar twernertwernermaven.repo.remote=http://www.ibiblio.org/maven,http://maven-plugins.sourceforge.net/maven maven.xdoc.date=left maven.xdoc.version=${pom.currentVersion} # use toplevel checkstyle configuration maven.checkstyle.properties=${basedir}/../conf/clirr_checks.xml maven.checkstyle.header.file=${basedir}/../conf/javaheader.txt maven.checkstyle.fail.on.violation=true # include api overview page, will work only with Maven version > 1.0-rc1 maven.javadoc.overview=${pom.build.sourceDirectory}/net/sf/clirr/overview.html # use the toplevel license file in subproject maven.license.licenseFile=${basedir}/../LICENSE.txt # hide irrelevant stuff from the nav bar maven.xdoc.poweredby.image= maven.xdoc.developmentProcessUrl= maven.junit.fork=true # tell tests where testinput is maven.junit.sysproperties=testinput testinput=${basedir}/target/testinput # class to execute from all-in-one javaapp maven.javaapp.mainclass=net.sf.clirr.cli.Clirr maven.javaapp.type=uber # used by test pregoal to generate test input, see maven.xml clirr.testlibs=testlib-v1, testlib-v2 ./clirr-0.6/core/src/0000755000175000017500000000000010316146510014301 5ustar twernertwerner./clirr-0.6/core/src/test/0000755000175000017500000000000010316146510015260 5ustar twernertwerner./clirr-0.6/core/src/test/net/0000755000175000017500000000000010316146510016046 5ustar twernertwerner./clirr-0.6/core/src/test/net/sf/0000755000175000017500000000000010316146510016456 5ustar twernertwerner./clirr-0.6/core/src/test/net/sf/clirr/0000755000175000017500000000000010316146510017571 5ustar twernertwerner./clirr-0.6/core/src/test/net/sf/clirr/ant/0000755000175000017500000000000010316146510020353 5ustar twernertwerner./clirr-0.6/core/src/test/net/sf/clirr/ant/ChangeCounterTest.java0000644000175000017500000000223110316146510024601 0ustar twernertwernerpackage net.sf.clirr.ant; import junit.framework.TestCase; import net.sf.clirr.core.ApiDifference; import net.sf.clirr.core.Severity; import net.sf.clirr.core.Message; public class ChangeCounterTest extends TestCase { public void testCorrectCounting() { // a dummy message object Message msg = new Message(0, false); ChangeCounter counter = new ChangeCounter(); counter.reportDiff(new ApiDifference(msg, Severity.WARNING, "Test", null, null, null)); counter.reportDiff(new ApiDifference(msg, Severity.ERROR, "Test", null, null, null)); counter.reportDiff(new ApiDifference(msg, Severity.INFO, "Test", null, null, null)); counter.reportDiff(new ApiDifference(msg, Severity.ERROR, "Test", null, null, null)); counter.reportDiff(new ApiDifference(msg, Severity.ERROR, "Test", null, null, null)); counter.reportDiff(new ApiDifference(msg, Severity.WARNING, "Test", null, null, null)); assertEquals("number of expected errors", 3, counter.getBinErrors()); assertEquals("number of expected warnings", 2, counter.getBinWarnings()); assertEquals("number of expected infos", 1, counter.getBinInfos()); } }./clirr-0.6/core/src/test/net/sf/clirr/core/0000755000175000017500000000000010316146510020521 5ustar twernertwerner./clirr-0.6/core/src/test/net/sf/clirr/core/internal/0000755000175000017500000000000010316146510022335 5ustar twernertwerner./clirr-0.6/core/src/test/net/sf/clirr/core/internal/checks/0000755000175000017500000000000010316146510023575 5ustar twernertwerner./clirr-0.6/core/src/test/net/sf/clirr/core/internal/checks/TestDiffListener.java0000644000175000017500000000706410316146510027665 0ustar twernertwernerpackage net.sf.clirr.core.internal.checks; import java.util.Set; import java.util.HashSet; import java.util.Iterator; import net.sf.clirr.core.internal.ApiDiffDispatcher; import net.sf.clirr.core.internal.checks.ExpectedDiff; import net.sf.clirr.core.ApiDifference; import net.sf.clirr.core.DiffListener; import net.sf.clirr.core.MessageTranslator; import junit.framework.TestCase; class TestDiffListener implements ApiDiffDispatcher, DiffListener { private MessageTranslator translator = new MessageTranslator(); private Set diffs = new HashSet(); public void fireDiff(ApiDifference difference) { diffs.add(difference); } public void checkExpected(ExpectedDiff[] expectedDiffs) { for (int i=0; inull * if no method was affected. * @param field the field name where the change occured, null * if no field was affected. */ public ExpectedDiff(String report, Severity severity, String clazz, String method, String field) { this(report, severity, severity, clazz, method, field); } /** * Create a new API difference representation. * * @param report a human readable string describing the change that was made, must be non-null. * @param binarySeverity the severity in terms of binary compatibility, must be non-null. * @param sourceSeverity the severity in terms of source code compatibility, must be non-null. * @param clazz the fully qualified class name where the change occured, must be non-null. * @param method the method signature of the method that changed, null * if no method was affected. * @param field the field name where the change occured, null * if no field was affected. */ public ExpectedDiff(String report, Severity binarySeverity, Severity sourceSeverity, String clazz, String method, String field) { checkNonNull(report); checkNonNull(binarySeverity); checkNonNull(sourceSeverity); checkNonNull(clazz); this.report = report; this.binaryCompatibilitySeverity = binarySeverity; this.sourceCompatibilitySeverity = sourceSeverity; this.affectedClass = clazz; this.affectedField = field; this.affectedMethod = method; } /** * Trivial utility method to verify that a specific object is non-null. */ private void checkNonNull(Object o) { if (o == null) { throw new IllegalArgumentException(); } } /** * {@inheritDoc} */ public String toString() { return report + " (" + binaryCompatibilitySeverity + ") - " + affectedClass + '[' + affectedField + '/' + affectedMethod + ']'; } /** * Returns true if the provided ApiDifference object matches the * expected value. */ public boolean matches(ApiDifference diff) { if (!report.equals(diff.getReport(translator))) { return false; } if (!binaryCompatibilitySeverity.equals(diff.getBinaryCompatibilitySeverity())) { return false; } if (!sourceCompatibilitySeverity.equals(diff.getSourceCompatibilitySeverity())) { return false; } final String otherClass = diff.getAffectedClass(); if (!affectedClass.equals(otherClass)) { return false; } final String otherMethod = diff.getAffectedMethod(); if (affectedMethod != null ? !affectedMethod.equals(otherMethod) : otherMethod != null) { return false; } final String otherField = diff.getAffectedField(); if (affectedField != null ? !affectedField.equals(otherField) : otherField != null) { return false; } return true; } } ./clirr-0.6/core/src/test/net/sf/clirr/core/internal/checks/AbstractCheckTestCase.java0000644000175000017500000000557610316146510030612 0ustar twernertwernerpackage net.sf.clirr.core.internal.checks; import junit.framework.TestCase; import net.sf.clirr.core.Checker; import net.sf.clirr.core.ClassSelector; import net.sf.clirr.core.ApiDifference; import net.sf.clirr.core.CheckerFactory; import net.sf.clirr.core.ClassFilter; import net.sf.clirr.core.internal.ClassChangeCheck; import net.sf.clirr.core.internal.bcel.BcelTypeArrayBuilder; import net.sf.clirr.core.spi.JavaType; import org.apache.bcel.util.ClassSet; import java.io.File; import java.net.URL; import java.net.URLClassLoader; /** * Abstract Baseclass to test individual Checks. * @author lkuehne */ public abstract class AbstractCheckTestCase extends TestCase { protected final File getTestInputDir() { // property is set in project.properties return new File(System.getProperty("testinput")); } protected void verify( Checker checker, ClassSet baseline, ClassSet current, ApiDifference[] expected) { } protected File[] getBaseLine() { return new File[]{ new File(getTestInputDir(), "testlib-v1.jar") }; } protected File[] getCurrent() { return new File[]{ new File(getTestInputDir(), "testlib-v2.jar") }; } protected void verify(ExpectedDiff[] expected) throws Exception { TestDiffListener tdl = new TestDiffListener(); Checker checker = CheckerFactory.createChecker(createCheck(tdl)); ClassFilter classSelector = createClassSelector(); final JavaType[] origClasses = BcelTypeArrayBuilder.createClassSet(getBaseLine(), new URLClassLoader(new URL[]{}), classSelector); final JavaType[] newClasses = BcelTypeArrayBuilder.createClassSet(getCurrent(), new URLClassLoader(new URL[]{}), classSelector); checker.reportDiffs(origClasses, newClasses); tdl.checkExpected(expected); } /** * Creates an object which selects the appropriate classes from the * test jars for this test. *

* This base implementation returns a selector which selects all classes * in the base "testlib" package (but no sub-packages). Tests which wish * to select different classes from the test jars should override this * method. */ protected ClassFilter createClassSelector() { // only check classes in the base "testlib" package of the jars ClassSelector classSelector = new ClassSelector(ClassSelector.MODE_IF); classSelector.addPackage("testlib"); return classSelector; } /** * Creates a check and sets it up so ApiDifferences are reported to the test diff listener. * * @param tdl the test diff listener that records the recognized api changes. * @return the confiured check */ protected abstract ClassChangeCheck createCheck(TestDiffListener tdl); } ./clirr-0.6/core/src/test/net/sf/clirr/core/internal/checks/ClassAddedRemovedTest.java0000644000175000017500000000320010316146510030604 0ustar twernertwernerpackage net.sf.clirr.core.internal.checks; import java.net.URL; import java.net.URLClassLoader; import net.sf.clirr.core.Checker; import net.sf.clirr.core.CheckerFactory; import net.sf.clirr.core.Severity; import net.sf.clirr.core.internal.ClassChangeCheck; import net.sf.clirr.core.internal.bcel.BcelTypeArrayBuilder; import net.sf.clirr.core.spi.JavaType; import net.sf.clirr.core.ClassFilter; public class ClassAddedRemovedTest extends AbstractCheckTestCase { public void testClassAddionOrRemovalIsReported() throws Exception { Checker checker = CheckerFactory.createChecker(null); TestDiffListener tld = new TestDiffListener(); checker.addDiffListener(tld); ClassFilter classSelector = createClassSelector(); final JavaType[] origClasses = BcelTypeArrayBuilder.createClassSet(getBaseLine(), new URLClassLoader(new URL[]{}), classSelector); final JavaType[] newClasses = BcelTypeArrayBuilder.createClassSet(getCurrent(), new URLClassLoader(new URL[]{}), classSelector); checker.reportDiffs(origClasses, newClasses); ExpectedDiff[] expected = new ExpectedDiff[] { new ExpectedDiff("Class testlib.AddedClass added", Severity.INFO, "testlib.AddedClass", null, null), new ExpectedDiff("Class testlib.RemovedClass removed", Severity.ERROR, "testlib.RemovedClass", null, null), }; tld.checkExpected(expected); } protected ClassChangeCheck createCheck(TestDiffListener tdl) { // changes are reported directly by the Checker return null; } } ./clirr-0.6/core/src/test/net/sf/clirr/core/internal/checks/ClassScopeCheckTest.java0000644000175000017500000000614510316146510030303 0ustar twernertwernerpackage net.sf.clirr.core.internal.checks; import net.sf.clirr.core.internal.ClassChangeCheck; import net.sf.clirr.core.Severity; import net.sf.clirr.core.ClassSelector; import net.sf.clirr.core.ScopeSelector; import net.sf.clirr.core.ClassFilter; import net.sf.clirr.core.internal.checks.ClassScopeCheck; import net.sf.clirr.core.internal.checks.AbstractCheckTestCase; import net.sf.clirr.core.spi.Scope; /** * Tests for the ClassScopeCheck test. * * @author Simon Kitching */ public class ClassScopeCheckTest extends AbstractCheckTestCase { public void testAccessChangesAreReported() throws Exception { ExpectedDiff[] expected = new ExpectedDiff[] { new ExpectedDiff("Decreased visibility of class from public to protected", Severity.ERROR, "testlib.scope.ClassScopeChange$A2", null, null), new ExpectedDiff("Decreased visibility of class from public to package", Severity.ERROR, "testlib.scope.ClassScopeChange$A3", null, null), new ExpectedDiff("Decreased visibility of class from public to private", Severity.ERROR, "testlib.scope.ClassScopeChange$A4", null, null), new ExpectedDiff("Increased visibility of class from protected to public", Severity.INFO, "testlib.scope.ClassScopeChange$B2", null, null), new ExpectedDiff("Decreased visibility of class from protected to package", Severity.ERROR, "testlib.scope.ClassScopeChange$B3", null, null), new ExpectedDiff("Decreased visibility of class from protected to private", Severity.ERROR, "testlib.scope.ClassScopeChange$B4", null, null), new ExpectedDiff("Increased visibility of class from package to public", Severity.INFO, "testlib.scope.ClassScopeChange$C2", null, null), new ExpectedDiff("Increased visibility of class from package to protected", Severity.INFO, "testlib.scope.ClassScopeChange$C3", null, null), // package->private is not an error, just an info, because we never report error for package or private diffs new ExpectedDiff("Decreased visibility of class from package to private", Severity.INFO, "testlib.scope.ClassScopeChange$C4", null, null), new ExpectedDiff("Increased visibility of class from private to public", Severity.INFO, "testlib.scope.ClassScopeChange$D2", null, null), new ExpectedDiff("Increased visibility of class from private to protected", Severity.INFO, "testlib.scope.ClassScopeChange$D3", null, null), new ExpectedDiff("Increased visibility of class from private to package", Severity.INFO, "testlib.scope.ClassScopeChange$D4", null, null), }; verify(expected); } protected ClassChangeCheck createCheck(TestDiffListener tdl) { ScopeSelector scopeSelector = new ScopeSelector(Scope.PRIVATE); return new ClassScopeCheck(tdl, scopeSelector); } protected ClassFilter createClassSelector() { // only check the testlib/scope/ClassScopeChange class. ClassSelector classSelector = new ClassSelector(ClassSelector.MODE_IF); classSelector.addClass("testlib.scope.ClassScopeChange"); return classSelector; } }./clirr-0.6/core/src/test/net/sf/clirr/core/internal/checks/MethodSetCheckTest.java0000644000175000017500000001221010316146510030126 0ustar twernertwernerpackage net.sf.clirr.core.internal.checks; import net.sf.clirr.core.internal.ClassChangeCheck; import net.sf.clirr.core.Severity; import net.sf.clirr.core.ScopeSelector; import net.sf.clirr.core.internal.checks.MethodSetCheck; import net.sf.clirr.core.internal.checks.AbstractCheckTestCase; import net.sf.clirr.core.internal.checks.ExpectedDiff; /** * TODO: Docs. * * @author lkuehne */ public class MethodSetCheckTest extends AbstractCheckTestCase { public void testMethodCheck() throws Exception { ExpectedDiff[] expected = new ExpectedDiff[] { // method addition and removal new ExpectedDiff("Method 'public void removedMethod(java.lang.String)' has been removed", Severity.ERROR, "testlib.MethodsChange", "public void removedMethod(java.lang.String)", null), new ExpectedDiff("Accessibility of method 'public int getPriv2()' has been decreased from public to private", Severity.ERROR, "testlib.MethodsChange", "public int getPriv2()", null), new ExpectedDiff("Method 'protected MethodsChange(int, boolean)' has been added", Severity.INFO, "testlib.MethodsChange", "protected MethodsChange(int, boolean)", null), new ExpectedDiff("Method 'public java.lang.Long getPrivSquare()' has been added", Severity.INFO, "testlib.MethodsChange", "public java.lang.Long getPrivSquare()", null), new ExpectedDiff("Method 'public void moveToSuper()' has been added", Severity.INFO, "testlib.ComplexMethodMoveBase", "public void moveToSuper()", null), new ExpectedDiff("Method 'public void moveToSuper()' is now implemented in superclass testlib.ComplexMethodMoveBase", Severity.INFO, "testlib.ComplexMethodMoveSub", "public void moveToSuper()", null), new ExpectedDiff("Method 'public void method()' has been removed, but an inherited definition exists.", Severity.INFO, "testlib.AbstractImpl", "public void method()", null), // Constructor changes new ExpectedDiff("Parameter 1 of 'protected MethodsChange(int)' has changed its type to java.lang.Integer", Severity.ERROR, "testlib.MethodsChange", "protected MethodsChange(int)", null), // return type changes new ExpectedDiff("Return type of method 'public java.lang.Number getPrivAsNumber()' has been changed to java.lang.Integer", Severity.ERROR, "testlib.MethodsChange", "public java.lang.Number getPrivAsNumber()", null), // TODO: INFO if method is final new ExpectedDiff("Return type of method 'public java.lang.Integer getPrivAsInteger()' has been changed to java.lang.Number", Severity.ERROR, "testlib.MethodsChange", "public java.lang.Integer getPrivAsInteger()", null), // parameter list changes // Note: This is the current behaviour, not necessarily the spec of the desired behaviour // TODO: need to check assignability of types (and check if method or class is final?) new ExpectedDiff("In method 'public void printPriv()' the number of arguments has changed", Severity.ERROR, "testlib.MethodsChange", "public void printPriv()", null), new ExpectedDiff("Parameter 1 of 'public void strengthenParamType(java.lang.Object)' has changed its type to java.lang.String", Severity.ERROR, "testlib.MethodsChange", "public void strengthenParamType(java.lang.Object)", null), new ExpectedDiff("Parameter 1 of 'public void weakenParamType(java.lang.String)' has changed its type to java.lang.Object", Severity.ERROR, "testlib.MethodsChange", "public void weakenParamType(java.lang.String)", null), new ExpectedDiff("Parameter 1 of 'public void changeParamType(java.lang.String)' has changed its type to java.lang.Integer", Severity.ERROR, "testlib.MethodsChange", "public void changeParamType(java.lang.String)", null), // deprecation changes new ExpectedDiff("Method 'public void becomesDeprecated()' has been deprecated", Severity.INFO, "testlib.MethodsChange", "public void becomesDeprecated()", null), new ExpectedDiff("Method 'public void becomesUndeprecated()' is no longer deprecated", Severity.INFO, "testlib.MethodsChange", "public void becomesUndeprecated()", null), // modifier changes new ExpectedDiff("Method 'public void becomesFinal()' is now final", Severity.ERROR, "testlib.MethodsChange", "public void becomesFinal()", null), new ExpectedDiff("Method 'public void becomesNonFinal()' is no longer final", Severity.INFO, "testlib.MethodsChange", "public void becomesNonFinal()", null), // declared exceptions // TODO }; verify(expected); } protected final ClassChangeCheck createCheck(TestDiffListener tdl) { return new MethodSetCheck(tdl, new ScopeSelector()); } } ./clirr-0.6/core/src/test/net/sf/clirr/core/internal/checks/ClassModifierCheckTest.java0000644000175000017500000000246110316146510030765 0ustar twernertwernerpackage net.sf.clirr.core.internal.checks; import net.sf.clirr.core.internal.ClassChangeCheck; import net.sf.clirr.core.Severity; import net.sf.clirr.core.ClassSelector; import net.sf.clirr.core.ClassFilter; import net.sf.clirr.core.internal.checks.ClassModifierCheck; import net.sf.clirr.core.internal.checks.AbstractCheckTestCase; /** * Tests for the ClassModifierCheck class. */ public class ClassModifierCheckTest extends AbstractCheckTestCase { public void testAll() throws Exception { ExpectedDiff[] expected = new ExpectedDiff[] { new ExpectedDiff("Added final modifier to class", Severity.ERROR, "testlib.modifiers.NonFinalBecomesFinal", null, null), new ExpectedDiff("Added final modifier to class, but class was effectively final anyway", Severity.INFO, "testlib.modifiers.EffectivelyFinal", null, null), }; verify(expected); } protected ClassChangeCheck createCheck(TestDiffListener tdl) { return new ClassModifierCheck(tdl); } protected ClassFilter createClassSelector() { // only apply the check to classes in the testlib.modifiers package. ClassSelector classSelector = new ClassSelector(ClassSelector.MODE_IF); classSelector.addPackage("testlib.modifiers"); return classSelector; } } ./clirr-0.6/core/src/test/net/sf/clirr/core/internal/checks/InterfaceSetCheckTest.java0000644000175000017500000000222510316146510030613 0ustar twernertwernerpackage net.sf.clirr.core.internal.checks; import net.sf.clirr.core.Severity; import net.sf.clirr.core.internal.ClassChangeCheck; public class InterfaceSetCheckTest extends AbstractCheckTestCase { public void testHierarchyChangesAreReported() throws Exception { ExpectedDiff[] expected = new ExpectedDiff[] { new ExpectedDiff("Added java.awt.event.WindowListener to the set of implemented interfaces", Severity.INFO, "testlib.ChangingHierarchy", null, null), new ExpectedDiff("Added java.awt.event.WindowFocusListener to the set of implemented interfaces", Severity.INFO, "testlib.ChangingHierarchy", null, null), new ExpectedDiff("Added java.awt.event.WindowStateListener to the set of implemented interfaces", Severity.INFO, "testlib.ChangingHierarchy", null, null), new ExpectedDiff("Removed java.awt.event.MouseListener from the set of implemented interfaces", Severity.ERROR, "testlib.ChangingHierarchy", null, null), }; verify(expected); } protected ClassChangeCheck createCheck(TestDiffListener tdl) { return new InterfaceSetCheck(tdl); } } ./clirr-0.6/core/src/test/net/sf/clirr/core/internal/checks/GenderChangeCheckTest.java0000644000175000017500000000163110316146510030551 0ustar twernertwernerpackage net.sf.clirr.core.internal.checks; import net.sf.clirr.core.Severity; import net.sf.clirr.core.internal.ClassChangeCheck; import net.sf.clirr.core.internal.checks.GenderChangeCheck; import net.sf.clirr.core.internal.checks.AbstractCheckTestCase; import net.sf.clirr.core.internal.checks.ExpectedDiff; public class GenderChangeCheckTest extends AbstractCheckTestCase { public void testGenderChangeCheckTest() throws Exception { ExpectedDiff[] expected = new ExpectedDiff[] { new ExpectedDiff("Changed from class to interface", Severity.ERROR, "testlib.ClassBecomesInterface", null, null), new ExpectedDiff("Changed from interface to class", Severity.ERROR, "testlib.InterfaceBecomesClass", null, null), }; verify(expected); } protected final ClassChangeCheck createCheck(TestDiffListener tdl) { return new GenderChangeCheck(tdl); } } ./clirr-0.6/core/src/test/net/sf/clirr/core/internal/checks/ClassHierarchyCheckTest.java0000644000175000017500000000214010316146510031137 0ustar twernertwernerpackage net.sf.clirr.core.internal.checks; import net.sf.clirr.core.internal.ClassChangeCheck; import net.sf.clirr.core.internal.checks.ClassHierarchyCheck; import net.sf.clirr.core.internal.checks.AbstractCheckTestCase; import net.sf.clirr.core.Severity; /** * @author lkuehne */ public class ClassHierarchyCheckTest extends AbstractCheckTestCase { public void testHierarchyChangesAreReported() throws Exception { ExpectedDiff[] expected = new ExpectedDiff[] { new ExpectedDiff("Added java.util.NoSuchElementException to the list of superclasses", Severity.WARNING, "testlib.ApplicationException", null, null), new ExpectedDiff("Removed java.awt.event.MouseAdapter from the list of superclasses", Severity.ERROR, "testlib.ChangingHierarchy", null, null), new ExpectedDiff("Added java.awt.event.WindowAdapter to the list of superclasses", Severity.INFO, "testlib.ChangingHierarchy", null, null), }; verify(expected); } protected ClassChangeCheck createCheck(TestDiffListener tdl) { return new ClassHierarchyCheck(tdl); } } ./clirr-0.6/core/src/test/net/sf/clirr/core/internal/CoIteratorTest.java0000644000175000017500000000347610316146510026125 0ustar twernertwernerpackage net.sf.clirr.core.internal; import java.util.ArrayList; import junit.framework.TestCase; import net.sf.clirr.core.internal.CoIterator; /** * Tests for the CoIterator class. */ public class CoIteratorTest extends TestCase { public void testBasics() { ArrayList coll1 = new ArrayList(); ArrayList coll2 = new ArrayList(); coll1.add("delta"); coll1.add("beta"); coll1.add("echo"); coll1.add("foxtrot"); coll2.add("delta"); coll2.add("beta"); coll2.add("foxtrot"); coll2.add("alpha"); coll2.add("golf"); coll2.add("hotel"); CoIterator iter = new CoIterator(null, coll1, coll2); ArrayList lefts = new ArrayList(); ArrayList rights = new ArrayList(); while (iter.hasNext()) { iter.next(); lefts.add(iter.getLeft()); rights.add(iter.getRight()); } int coll1Size = coll1.size(); int coll2Size = coll2.size(); assertEquals(7, lefts.size()); assertEquals(7, rights.size()); assertEquals("lefts should not have alpha", null, lefts.get(0)); assertEquals("rights should have alpha", "alpha", rights.get(0)); assertEquals("lefts should have beta", "beta", lefts.get(1)); assertEquals("rights should have beta", "beta", rights.get(1)); assertEquals("delta", lefts.get(2)); assertEquals("delta", rights.get(2)); assertEquals("echo", lefts.get(3)); assertEquals(null, rights.get(3)); assertEquals("foxtrot", lefts.get(4)); assertEquals("foxtrot", rights.get(4)); assertEquals(null, lefts.get(5)); assertEquals("golf", rights.get(5)); assertEquals(null, lefts.get(6)); assertEquals("hotel", rights.get(6)); } } ./clirr-0.6/core/src/test/net/sf/clirr/core/MessageTest.java0000644000175000017500000000375310316146510023620 0ustar twernertwernerpackage net.sf.clirr.core; import java.util.Locale; import junit.framework.TestCase; import net.sf.clirr.core.MessageManager; import net.sf.clirr.core.MessageTranslator; /** * Tests for the Message and MessageManager classes. *

* It is assumed here that the other unit tests have forced every Check * class to be loaded into memory, hence all the static Message objects * have been created and registered with the MessageManager. */ public class MessageTest extends TestCase { /** * This test verifies that none of the check classes has used * a message-id which is already in use elsewhere. *

* It is assumed that instantiating the Checker class causes every * check class to be loaded, which in turn causes every Message * object (which are expected to be static members of checks) to be created. */ public void testUnique() { Checker checker = CheckerFactory.createChecker(); MessageManager.getInstance().checkUnique(); } /** * This test verifies that the default resource bundle contains an * entry for every known message. *

* Unfortunately, it is not possible to check whether, for example, * the "de" locale has a complete set of translations. This is because * the ResourceBundle implementation simply returns a string from an * inherited "parent" resource bundle if the key is not found in a * locale-specific bundle, and there is no way of telling which * bundle the message was retrieved from. */ public void testComplete() { Checker checker = CheckerFactory.createChecker(); java.util.Collection messages = MessageManager.getInstance().getMessages(); // there are at least 10 messages in the system assertTrue(messages.size() > 10); // check the english locale MessageTranslator translator = new MessageTranslator(); translator.setLocale(Locale.ENGLISH); translator.checkComplete(messages); } } ./clirr-0.6/core/src/test/net/sf/clirr/core/CheckerFactory.java0000644000175000017500000000076410316146510024267 0ustar twernertwernerpackage net.sf.clirr.core; import net.sf.clirr.core.internal.ClassChangeCheck; import net.sf.clirr.core.Checker; /** * Provides a way for check tests to create a checker via the package private * Checker constructor, even if the tests are not in the same package. */ public class CheckerFactory { public static Checker createChecker(ClassChangeCheck check) { return new Checker(check); } public static Checker createChecker() { return new Checker(); } } ./clirr-0.6/core/src/testinput/0000755000175000017500000000000010316146510016340 5ustar twernertwerner./clirr-0.6/core/src/testinput/testlib-v2/0000755000175000017500000000000010316146510020333 5ustar twernertwerner./clirr-0.6/core/src/testinput/testlib-v2/testlib/0000755000175000017500000000000010316146510022001 5ustar twernertwerner./clirr-0.6/core/src/testinput/testlib-v2/testlib/scope/0000755000175000017500000000000010316146510023112 5ustar twernertwerner./clirr-0.6/core/src/testinput/testlib-v2/testlib/scope/ClassScopeChange.java0000644000175000017500000000242210316146510027122 0ustar twernertwernerpackage testlib.scope; public class ClassScopeChange { // public class is unchanged public static class A1 {} // public class becomes protected protected static class A2 {} // public class becomes package static class A3 {} // public class becomes private private static class A4 {} // protected class is unchanged protected static class B1 {} // protected class becomes public public static class B2 {} // protected class becomes package static class B3 {} // protected class becomes private private static class B4 {} // package class is unchanged static class C1 {} // package class becomes public public static class C2 {} // package class becomes protected protected static class C3 {} // package class becomes private private static class C4 {} // private class is unchanged private static class D1 {} // private class becomes public public static class D2 {} // private class becomes protected protected static class D3 {} // private class becomes package static class D4 {} // unchanged scope of class defined inside method body private void method1() { class E1 {}; E1 e1 = new E1(); } } ./clirr-0.6/core/src/testinput/testlib-v2/testlib/InterfaceBecomesClass.java0000644000175000017500000000007110316146510027026 0ustar twernertwernerpackage testlib; public class InterfaceBecomesClass { } ./clirr-0.6/core/src/testinput/testlib-v2/testlib/BaseMembers.java0000644000175000017500000000015210316146510025027 0ustar twernertwernerpackage testlib; public class BaseMembers { public static int base1 = 0; public int base2 = 0; } ./clirr-0.6/core/src/testinput/testlib-v2/testlib/ComplexMethodMoveBase.java0000644000175000017500000000020710316146510027035 0ustar twernertwernerpackage testlib; public class ComplexMethodMoveBase { // moved from ComplexMethodMoveSub in v1 public void moveToSuper() {} } ./clirr-0.6/core/src/testinput/testlib-v2/testlib/ApplicationException.java0000644000175000017500000000014110316146510026762 0ustar twernertwernerpackage testlib; public class ApplicationException extends java.util.NoSuchElementException { } ./clirr-0.6/core/src/testinput/testlib-v2/testlib/modifiers/0000755000175000017500000000000010316146510023762 5ustar twernertwerner./clirr-0.6/core/src/testinput/testlib-v2/testlib/modifiers/EffectivelyFinal.java0000644000175000017500000000130410316146510030042 0ustar twernertwernerpackage testlib.modifiers; /** * Because this class has a package-scope constructor, no subclass of this * class can ever be created. And because of that, it is not a problem if * this class is declared "final" in a later version. *

* Classes with only private constructors are commonly used to implement * an "enumerated type" in java, as is done here. */ public final class EffectivelyFinal { int val; public static final EffectivelyFinal ZERO = new EffectivelyFinal(0); public static final EffectivelyFinal ONE = new EffectivelyFinal(1); private EffectivelyFinal(int i) { val = i; } public int getValue() { return val; } } ./clirr-0.6/core/src/testinput/testlib-v2/testlib/modifiers/NonFinalBecomesFinal.java0000644000175000017500000000050610316146510030602 0ustar twernertwernerpackage testlib.modifiers; /** * It is a binary compatibility error for a non-final class to become * final in a later version, because users may have created classes which * are derived from it. Such classes will fail to load with the new version * of the class present. */ public final class NonFinalBecomesFinal { } ./clirr-0.6/core/src/testinput/testlib-v2/testlib/ChangingHierarchy.java0000644000175000017500000000013210316146510026215 0ustar twernertwernerpackage testlib; public class ChangingHierarchy extends java.awt.event.WindowAdapter { } ./clirr-0.6/core/src/testinput/testlib-v2/testlib/MethodsChange.java0000644000175000017500000000341010316146510025353 0ustar twernertwernerpackage testlib; import java.io.IOException; public class MethodsChange { private int priv; public MethodsChange() { priv = 2; } protected MethodsChange(int initialpriv, boolean newArg) { priv = initialpriv; } protected MethodsChange(Integer initialpriv) { priv = initialpriv.intValue(); } public int getPriv() { return priv; } private int getPriv2() { return priv; } public Integer getPrivAsNumber() { return new Integer(priv); } public Number getPrivAsInteger() { return new Integer(priv); } public Long getPrivSquare() { return new Long(priv * priv); } public void printPriv(String prefix) { System.out.println(prefix + priv); } public void weakenParamType(Object s) { } public void strengthenParamType(String s) { } public void changeParamType(Integer x) { } public void throwIOException() throws Exception { throw new java.io.IOException(); } public void throwException() throws IOException { throw new java.io.IOException(); } public void throwException2() { } public void throwRuntimeException() { throw new RuntimeException(); } public void throwNoRuntimeException() throws RuntimeException { throw new RuntimeException(); } public void throwNoException() throws Exception { throw new Exception(); } /** * @deprecated this is a bad method. */ public void becomesDeprecated() { } /** * This method was previously deprecated. */ public void becomesUndeprecated() { } public void becomesNonFinal() { } public final void becomesFinal() { } } ./clirr-0.6/core/src/testinput/testlib-v2/testlib/ComplexMethodMoveSub.java0000644000175000017500000000012610316146510026714 0ustar twernertwernerpackage testlib; public class ComplexMethodMoveSub extends ComplexMethodMoveBase { } ./clirr-0.6/core/src/testinput/testlib-v2/testlib/AbstractImpl.java0000644000175000017500000000022410316146510025227 0ustar twernertwernerpackage testlib; public abstract class AbstractImpl implements BaseInterface { // public abstract void method(); not available here directly } ./clirr-0.6/core/src/testinput/testlib-v2/testlib/AddedClass.java0000644000175000017500000000015010316146510024627 0ustar twernertwernerpackage testlib; public class AddedClass { public static final int NEWCONSTANT_NOT_REPORTED = 0; } ./clirr-0.6/core/src/testinput/testlib-v2/testlib/ClassBecomesInterface.java0000644000175000017500000000007510316146510027032 0ustar twernertwernerpackage testlib; public interface ClassBecomesInterface { } ./clirr-0.6/core/src/testinput/testlib-v2/testlib/BaseInterface.java0000644000175000017500000000011010316146510025327 0ustar twernertwernerpackage testlib; public interface BaseInterface { void method(); } ./clirr-0.6/core/src/testinput/testlib-v2/testlib/MembersChange.java0000644000175000017500000000317310316146510025350 0ustar twernertwernerpackage testlib; public class MembersChange extends BaseMembers { public static int stat1 = 0; // same public static final int stat2 = 0; // added final public int stat3 = 0; // removed static protected static int stat4 = 0; // public -> protected private static int stat5 = 0; // public -> private static int stat6 = 0; // public -> package // removed stat7 public static int stat8 = 0; // new member public static final int fin1 = 0; // same protected static final int fin2 = 0; // public -> protected public final int fin3 = 0; // removed static public static int fin4 = 0; // removed final public static final int fin5 = 1; // changed compile time constant public static final boolean fin6 = Boolean.FALSE.booleanValue(); // removed value of compile time constant // public static final int fin7 = 7; // removed constant field public int pub1 = 0; public static int pub2 = 0; // added static public final int pub3 = 0; // added final public int pub4 = 0; // public int pub5 = 0; // removed non-constant field protected int prot1 = 0; protected int prot2 = 0; protected int prot3 = 0; protected int prot4 = 0; public String obj1 = new String(); // member type changed Object -> String public String obj2 = new String(); // member type changed Boolean -> String private int priv1 = 0; // same public int priv2 = 0; // private -> public } ./clirr-0.6/core/src/testinput/testlib-v1/0000755000175000017500000000000010316146510020332 5ustar twernertwerner./clirr-0.6/core/src/testinput/testlib-v1/testlib/0000755000175000017500000000000010316146510022000 5ustar twernertwerner./clirr-0.6/core/src/testinput/testlib-v1/testlib/scope/0000755000175000017500000000000010316146510023111 5ustar twernertwerner./clirr-0.6/core/src/testinput/testlib-v1/testlib/scope/ClassScopeChange.java0000644000175000017500000000261010316146510027120 0ustar twernertwernerpackage testlib.scope; public class ClassScopeChange { // public class is unchanged public static class A1 {} // public class becomes protected public static class A2 {} // public class becomes package public static class A3 {} // public class becomes private public static class A4 {} // public class is removed public static class A5 {} // protected class is unchanged protected static class B1 {} // protected class becomes public protected static class B2 {} // protected class becomes package protected static class B3 {} // protected class becomes private protected static class B4 {} // package class is unchanged static class C1 {} // package class becomes public static class C2 {} // package class becomes protected static class C3 {} // package class becomes private static class C4 {} // package class is removed static class C5 {} // private class is unchanged private static class D1 {} // private class becomes public private static class D2 {} // private class becomes protected private static class D3 {} // private class becomes package private static class D4 {} // unchanged scope of class defined inside method body private void method1() { class E1 {}; E1 e1 = new E1(); } } ./clirr-0.6/core/src/testinput/testlib-v1/testlib/InterfaceBecomesClass.java0000644000175000017500000000007510316146510027031 0ustar twernertwernerpackage testlib; public interface InterfaceBecomesClass { } ./clirr-0.6/core/src/testinput/testlib-v1/testlib/RemovedClass.java0000644000175000017500000000015210316146510025230 0ustar twernertwernerpackage testlib; public class RemovedClass { public static final int OLDCONSTANT_NOT_REPORTED = 0; } ./clirr-0.6/core/src/testinput/testlib-v1/testlib/BaseMembers.java0000644000175000017500000000015210316146510025026 0ustar twernertwernerpackage testlib; public class BaseMembers { public static int base1 = 0; public int base2 = 0; } ./clirr-0.6/core/src/testinput/testlib-v1/testlib/ComplexMethodMoveBase.java0000644000175000017500000000007110316146510027033 0ustar twernertwernerpackage testlib; public class ComplexMethodMoveBase { } ./clirr-0.6/core/src/testinput/testlib-v1/testlib/ApplicationException.java0000644000175000017500000000012110316146510026757 0ustar twernertwernerpackage testlib; public class ApplicationException extends RuntimeException { } ./clirr-0.6/core/src/testinput/testlib-v1/testlib/modifiers/0000755000175000017500000000000010316146510023761 5ustar twernertwerner./clirr-0.6/core/src/testinput/testlib-v1/testlib/modifiers/EffectivelyFinal.java0000644000175000017500000000127610316146510030051 0ustar twernertwernerpackage testlib.modifiers; /** * Because this class has a package-scope constructor, no subclass of this * class can ever be created. And because of that, it is not a problem if * this class is declared "final" in a later version. *

* Classes with only private constructors are commonly used to implement * an "enumerated type" in java, as is done here. */ public class EffectivelyFinal { int val; public static final EffectivelyFinal ZERO = new EffectivelyFinal(0); public static final EffectivelyFinal ONE = new EffectivelyFinal(1); private EffectivelyFinal(int i) { val = i; } public int getValue() { return val; } } ./clirr-0.6/core/src/testinput/testlib-v1/testlib/modifiers/NonFinalBecomesFinal.java0000644000175000017500000000050010316146510030573 0ustar twernertwernerpackage testlib.modifiers; /** * It is a binary compatibility error for a non-final class to become * final in a later version, because users may have created classes which * are derived from it. Such classes will fail to load with the new version * of the class present. */ public class NonFinalBecomesFinal { } ./clirr-0.6/core/src/testinput/testlib-v1/testlib/ChangingHierarchy.java0000644000175000017500000000013110316146510026213 0ustar twernertwernerpackage testlib; public class ChangingHierarchy extends java.awt.event.MouseAdapter { } ./clirr-0.6/core/src/testinput/testlib-v1/testlib/MethodsChange.java0000644000175000017500000000341410316146510025356 0ustar twernertwernerpackage testlib; import java.io.IOException; public class MethodsChange { private int priv; static { System.out.println("static initializer"); } { System.out.println("non-static initializer"); } public MethodsChange() { priv = 2; } protected MethodsChange(int initialpriv) { priv = initialpriv; } public int getPriv() { return priv; } public int getPriv2() { return priv; } public Integer getPrivAsInteger() { return new Integer(priv); } public Number getPrivAsNumber() { return new Integer(priv); } public void printPriv() { System.out.println(priv); } public void removedMethod(String x) { } public void weakenParamType(String s) { } public void strengthenParamType(Object s) { } public void changeParamType(String s) { } public void throwIOException() throws IOException { throw new java.io.IOException(); } public void throwException() throws Exception { throw new java.io.IOException(); } public void throwException2() throws Exception { throw new Exception(); } public void throwRuntimeException() throws RuntimeException { throw new RuntimeException(); } public void throwNoRuntimeException() { } public void throwNoException() { } /** This method will be deprecated in the later version. */ public void becomesDeprecated() { } /** * This method will be "undeprecated" in the later version. * * @deprecated this is a bad method. */ public void becomesUndeprecated() { } public final void becomesNonFinal() { } public void becomesFinal() { } } ./clirr-0.6/core/src/testinput/testlib-v1/testlib/ComplexMethodMoveSub.java0000644000175000017500000000024710316146510026717 0ustar twernertwernerpackage testlib; public class ComplexMethodMoveSub extends ComplexMethodMoveBase { // will move to ComplexMethodMoveBase in v2 public void moveToSuper() {} } ./clirr-0.6/core/src/testinput/testlib-v1/testlib/AbstractImpl.java0000644000175000017500000000016510316146510025232 0ustar twernertwernerpackage testlib; public abstract class AbstractImpl implements BaseInterface { public abstract void method(); } ./clirr-0.6/core/src/testinput/testlib-v1/testlib/ClassBecomesInterface.java0000644000175000017500000000007110316146510027025 0ustar twernertwernerpackage testlib; public class ClassBecomesInterface { } ./clirr-0.6/core/src/testinput/testlib-v1/testlib/BaseInterface.java0000644000175000017500000000011010316146510025326 0ustar twernertwernerpackage testlib; public interface BaseInterface { void method(); } ./clirr-0.6/core/src/testinput/testlib-v1/testlib/MembersChange.java0000644000175000017500000000167210316146510025351 0ustar twernertwernerpackage testlib; public class MembersChange extends BaseMembers { public static int stat1 = 0; public static int stat2 = 0; public static int stat3 = 0; public static int stat4 = 0; public static int stat5 = 0; public static int stat6 = 0; public static int stat7 = 0; public static final int fin1 = 0; public static final int fin2 = 0; public static final int fin3 = 0; public static final int fin4 = 0; public static final int fin5 = 0; public static final boolean fin6 = false; public static final int fin7 = 7; public int pub1 = 0; public int pub2 = 0; public int pub3 = 0; public int pub4 = 0; public int pub5 = 0; protected int prot1 = 0; protected int prot2 = 0; protected int prot3 = 0; protected int prot4 = 0; public Object obj1 = new String(); public Boolean obj2 = Boolean.FALSE; private int priv1 = 0; private int priv2 = 0; } ./clirr-0.6/core/src/conf/0000755000175000017500000000000010316146510015226 5ustar twernertwerner./clirr-0.6/core/src/conf/net/0000755000175000017500000000000010316146510016014 5ustar twernertwerner./clirr-0.6/core/src/conf/net/sf/0000755000175000017500000000000010316146510016424 5ustar twernertwerner./clirr-0.6/core/src/conf/net/sf/clirr/0000755000175000017500000000000010316146510017537 5ustar twernertwerner./clirr-0.6/core/src/conf/net/sf/clirr/core/0000755000175000017500000000000010316146510020467 5ustar twernertwerner./clirr-0.6/core/src/conf/net/sf/clirr/core/EventMessages_de.properties0000644000175000017500000001003410316146510026024 0ustar twernertwerner# Translations for messages generated by the check classes. # # {0} --> full class name of affected class # {1} --> full method prototype of affected method # {2} --> full field declaration of affected field # {3}..{n} --> check-specific parameters #----------------------------------------------------------------------------- # ClassScopeCheck messages #----------------------------------------------------------------------------- m1000=Sichtbarkeit der Klasse wurde von {3} auf {4} erweitert m1001=Sichtbarkeit der Klasse wurde von {3} auf {4} eingeschrnkt m1002=Kann Klassen-Scope in der alten Version nicht bestimmen: {3} m1003=Kann Klassen-Scope in der neuen Version nicht bestimmen: {3} #----------------------------------------------------------------------------- # GenderChangeCheck messages #----------------------------------------------------------------------------- m2000=Klasse ist jetzt ein Interface m2001=Interface ist jetzt eine Klasse #----------------------------------------------------------------------------- # ClassModifierCheck messages #----------------------------------------------------------------------------- m3000=Kann nicht herausfinden ob Klasse private ist m3001=Der 'final' Modifier der Klasse wurde entfernt m3002=Der Klasse wurde ein 'final' Modifier hinzugefgt, sie war bisher allerdings nicht ableitbar m3003=Der Klasse wurde ein 'final' Modifier hinzugefgt m3004=Der 'abstract' Modifier der Klasse wurde entfernt m3005=Der Klasse wurde ein 'abstract' Modifier hinzugefgt #----------------------------------------------------------------------------- # InterfaceSetCheck messages #----------------------------------------------------------------------------- m4000=Das Interface {3} wird jetzt implementiert m4001=Das Interface {3} wird nicht mehr implementiert #----------------------------------------------------------------------------- # ClassHierarchyCheck messages #----------------------------------------------------------------------------- m5000=Neue Oberklasse {3} m5001={3} ist keine Oberklasse mehr #----------------------------------------------------------------------------- # FieldSetCheck messages #----------------------------------------------------------------------------- m6000={3} field {2} hinzugefgt m6001=Das Feld {2} wurde entfernt m6002=Der Wert von Feld {2} ist keine Compilezeit-Konstante mehr m6003=Der Wert der Compilezeit-Konstanten {2} wurde gendert m6004=Der Typ des Feldes {2} wurde von {3} zu {4} gendert m6005=Das Feld {2} ist nicht mehr final m6006=Das Feld {2} ist jetzt final m6007=Das Feld {2} ist nicht mehr static m6008=Das Feld {2} ist jetzt static m6009=Sichtbarkeit des Feldes {2} wurde von {3} auf {4} erhht m6010=Sichtbarkeit des Feldes {2} wurde von {3} auf {4} herabgesetzt #----------------------------------------------------------------------------- # MethodSetCheck messages #----------------------------------------------------------------------------- # MethodSetCheck messages m7000=Die Methode ''{1}'' ist jetzt durch die Oberklasse {3} implementiert m7001=Die abstrakte methode ''{1}'' ist jetzt im implementierten Interface {3} spezifiziert m7002=Method ''{1}'' wurde entfernt #m7003 no longer used m7004=In der Methode ''{1}'' hat sich die Anzahl der Argumente gendert m7005=Parametertyp {3} von ''{1}'' wurde zu {4} gendert m7006=Rckgabetyp der Methode ''{1}'' wurde zu {3} gendert m7007=Methode ''{1}'' wurde als deprecated markiert m7008=Methode ''{1}'' ist nicht mehr deprecated m7009=Sichtbarkeit von Methode ''{1}'' wurde von {3} auf {4} reduziert m7010=Sichtbarkeit der Methode ''{1}'' wurde von {3} auf {4} erhht m7011=Methode ''{1}'' wurde hinzugefgt m7012=Methode ''{1}'' wurde zum Interface hinzugefgt m7013=Abstrakte Methode ''{1}'' wurde hinzugefgt m7014=Methode ''{1}'' ist jetzt final m7015=Methode ''{1}'' ist nicht mehr final #----------------------------------------------------------------------------- # Core Check messages #----------------------------------------------------------------------------- m8000=Klasse {0} wurde hinzugefgt m8001=Klasse {0} wurde entfernt ./clirr-0.6/core/src/conf/net/sf/clirr/core/EventMessages_en.properties0000644000175000017500000001037310316146510026044 0ustar twernertwerner# Translations for messages generated by the check classes. # # {0} --> full class name of affected class # {1} --> full method prototype of affected method # {2} --> full field declaration of affected field # {3}..{n} --> check-specific parameters #----------------------------------------------------------------------------- # ClassScopeCheck messages #----------------------------------------------------------------------------- m1000=Increased visibility of class from {3} to {4} m1001=Decreased visibility of class from {3} to {4} m1002=Unable to determine class scope: {3} in old class version m1003=Unable to determine class scope: {3} in new class version #----------------------------------------------------------------------------- # GenderChangeCheck messages #----------------------------------------------------------------------------- m2000=Changed from class to interface m2001=Changed from interface to class #----------------------------------------------------------------------------- # ClassModifierCheck messages #----------------------------------------------------------------------------- m3000=Unable to determine whether class is private m3001=Removed final modifier from class m3002=Added final modifier to class, but class was effectively final anyway m3003=Added final modifier to class m3004=Removed abstract modifier from class m3005=Added abstract modifier to class #----------------------------------------------------------------------------- # InterfaceSetCheck messages #----------------------------------------------------------------------------- m4000=Added {3} to the set of implemented interfaces m4001=Removed {3} from the set of implemented interfaces #----------------------------------------------------------------------------- # ClassHierarchyCheck messages #----------------------------------------------------------------------------- m5000=Added {3} to the list of superclasses m5001=Removed {3} from the list of superclasses #----------------------------------------------------------------------------- # FieldSetCheck messages #----------------------------------------------------------------------------- m6000=Added {3} field {2} m6001=Removed field {2} m6002=Value of field {2} is no longer a compile-time constant m6003=Value of compile-time constant {2} has been changed m6004=Changed type of field {2} from {3} to {4} m6005=Field {2} is now non-final m6006=Field {2} is now final m6007=Field {2} is now non-static m6008=Field {2} is now static m6009=Accessibility of field {2} has been increased from {3} to {4} m6010=Accessibility of field {2} has been weakened from {3} to {4} m6011=Field {2} has been removed, but it was previously a constant #----------------------------------------------------------------------------- # MethodSetCheck messages #----------------------------------------------------------------------------- m7000=Method ''{1}'' is now implemented in superclass {3} m7001=Abstract method ''{1}'' is now specified by implemented interface {3} m7002=Method ''{1}'' has been removed m7003=Method ''{1}'' has been removed, but an inherited definition exists. m7004=In method ''{1}'' the number of arguments has changed m7005=Parameter {3} of ''{1}'' has changed its type to {4} m7006=Return type of method ''{1}'' has been changed to {3} m7007=Method ''{1}'' has been deprecated m7008=Method ''{1}'' is no longer deprecated m7009=Accessibility of method ''{1}'' has been decreased from {3} to {4} m7010=Accessibility of method ''{1}'' has been increased from {3} to {4} m7011=Method ''{1}'' has been added m7012=Method ''{1}'' has been added to an interface m7013=Abstract method ''{1}'' has been added m7014=Method ''{1}'' is now final m7015=Method ''{1}'' is no longer final #----------------------------------------------------------------------------- # Core Check messages #----------------------------------------------------------------------------- m8000=Class {0} added m8001=Class {0} removed #----------------------------------------------------------------------------- # Assorted messages #----------------------------------------------------------------------------- m9000=Unable to determine the accessibility of class {0}. This generally \ means that an invalid jarfile or classpath has been provided to the \ clirr program. ./clirr-0.6/core/src/conf/clirrtask.properties0000644000175000017500000000003710316146510021342 0ustar twernertwernerclirr=net.sf.clirr.ant.AntTask ./clirr-0.6/core/src/java/0000755000175000017500000000000010316146510015222 5ustar twernertwerner./clirr-0.6/core/src/java/net/0000755000175000017500000000000010316146510016010 5ustar twernertwerner./clirr-0.6/core/src/java/net/sf/0000755000175000017500000000000010316146510016420 5ustar twernertwerner./clirr-0.6/core/src/java/net/sf/clirr/0000755000175000017500000000000010316146510017533 5ustar twernertwerner./clirr-0.6/core/src/java/net/sf/clirr/ant/0000755000175000017500000000000010316146510020315 5ustar twernertwerner./clirr-0.6/core/src/java/net/sf/clirr/ant/AntLogger.java0000644000175000017500000000422110316146510023041 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.ant; import java.util.HashMap; import java.util.Map; import net.sf.clirr.core.DiffListenerAdapter; import net.sf.clirr.core.Severity; import net.sf.clirr.core.MessageTranslator; import net.sf.clirr.core.ApiDifference; import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; final class AntLogger extends DiffListenerAdapter { private Task task; private MessageTranslator translator = new MessageTranslator(); private Map severityPrioMap = new HashMap(3); AntLogger(Task task) { this.task = task; severityPrioMap.put(Severity.INFO, new Integer(Project.MSG_INFO)); severityPrioMap.put(Severity.WARNING, new Integer(Project.MSG_WARN)); severityPrioMap.put(Severity.ERROR, new Integer(Project.MSG_ERR)); } public void reportDiff(ApiDifference difference) { final Severity severity = difference.getMaximumSeverity(); final Integer prio = (Integer) severityPrioMap.get(severity); task.log( severity.toString() + ": " + difference.getAffectedClass() + ": " + difference.getReport(translator), prio.intValue()); } } ./clirr-0.6/core/src/java/net/sf/clirr/ant/AntTask.java0000644000175000017500000002437610316146510022541 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.ant; import java.io.File; import java.io.IOException; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import net.sf.clirr.core.Checker; import net.sf.clirr.core.CheckerException; import net.sf.clirr.core.ClassFilter; import net.sf.clirr.core.ClassSelector; import net.sf.clirr.core.PlainDiffListener; import net.sf.clirr.core.XmlDiffListener; import net.sf.clirr.core.internal.ClassLoaderUtil; import net.sf.clirr.core.internal.bcel.BcelTypeArrayBuilder; import net.sf.clirr.core.spi.JavaType; import org.apache.bcel.classfile.JavaClass; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; import org.apache.tools.ant.types.FileSet; import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.PatternSet; /** * Implements the Clirr ant task. * @author lkuehne */ public final class AntTask extends Task { private static final String FORMATTER_TYPE_PLAIN = "plain"; private static final String FORMATTER_TYPE_XML = "xml"; /** * Output formater. */ public static final class Formatter { private String type = null; private String outFile = null; public String getOutFile() { return outFile; } public void setOutFile(String outFile) { this.outFile = outFile; } public String getType() { return type; } public void setType(String type) { String lowerCase = type.toLowerCase(); if (!lowerCase.equals(FORMATTER_TYPE_XML) && !lowerCase.equals(FORMATTER_TYPE_PLAIN)) { throw new BuildException( "Illegal formatter type, only plain and xml are supported"); } this.type = type; } } /** * Class Filter that returns the logical "and" of two underlying class filters. */ private static class CompoundClassFilter implements ClassFilter { private final ClassFilter patternSetFilter; private final ClassFilter scopeSelector; public CompoundClassFilter(ClassFilter patternSetFilter, ClassFilter scopeSelector) { this.patternSetFilter = patternSetFilter; this.scopeSelector = scopeSelector; } public boolean isSelected(JavaClass clazz) { return patternSetFilter.isSelected(clazz) && scopeSelector.isSelected(clazz); } } private FileSet origFiles = null; private FileSet newFiles = null; private Path newClassPath = null; private Path origClassPath = null; private boolean failOnBinError = true; private boolean failOnBinWarning = false; private boolean failOnSrcError = true; private boolean failOnSrcWarning = false; private List formatters = new LinkedList(); private List patternSets = new LinkedList(); public Path createNewClassPath() { if (newClassPath == null) { newClassPath = new Path(getProject()); } return newClassPath.createPath(); } public void setNewClassPath(Path path) { if (newClassPath == null) { newClassPath = path; } else { newClassPath.append(path); } } public Path createOrigClassPath() { if (origClassPath == null) { origClassPath = new Path(getProject()); } return origClassPath.createPath(); } public void setOrigClassPath(Path path) { if (origClassPath == null) { origClassPath = path; } else { origClassPath.append(path); } } public void addOrigFiles(FileSet origFiles) { if (this.origFiles != null) { throw new BuildException(); } this.origFiles = origFiles; } public void addNewFiles(FileSet newFiles) { if (this.newFiles != null) { throw new BuildException(); } this.newFiles = newFiles; } public void setFailOnBinError(boolean failOnBinError) { this.failOnBinError = failOnBinError; } public void setFailOnBinWarning(boolean failOnBinWarning) { this.failOnBinWarning = failOnBinWarning; } public void setFailOnSrcError(boolean failOnSrcError) { this.failOnSrcError = failOnSrcError; } public void setFailOnSrcWarning(boolean failOnSrcWarning) { this.failOnSrcWarning = failOnSrcWarning; } public void addFormatter(Formatter formatter) { formatters.add(formatter); } public void addApiClasses(PatternSet set) { patternSets.add(set); } public void execute() { log("Running Clirr, built from tag $Name: RELEASE_CLIRR_0_6 $", Project.MSG_VERBOSE); if (origFiles == null || newFiles == null) { throw new BuildException( "Missing nested filesets origFiles and newFiles.", getLocation()); } if (newClassPath == null) { newClassPath = new Path(getProject()); } if (origClassPath == null) { origClassPath = new Path(getProject()); } final File[] origJars = scanFileSet(origFiles); final File[] newJars = scanFileSet(newFiles); if (origJars.length == 0) { throw new BuildException( "No files in nested fileset origFiles - nothing to check!" + " Please check your fileset specification."); } if (newJars.length == 0) { throw new BuildException( "No files in nested fileset newFiles - nothing to check!" + " Please check your fileset specification."); } final ClassLoader origThirdPartyLoader = createClasspathLoader(origClassPath); final ClassLoader newThirdPartyLoader = createClasspathLoader(newClassPath); final Checker checker = new Checker(); final ChangeCounter counter = new ChangeCounter(); boolean formattersWriteToStdOut = false; for (Iterator it = formatters.iterator(); it.hasNext();) { Formatter formatter = (Formatter) it.next(); final String type = formatter.getType(); final String outFile = formatter.getOutFile(); formattersWriteToStdOut = formattersWriteToStdOut || outFile == null; try { if (FORMATTER_TYPE_PLAIN.equals(type)) { checker.addDiffListener(new PlainDiffListener(outFile)); } else if (FORMATTER_TYPE_XML.equals(type)) { checker.addDiffListener(new XmlDiffListener(outFile)); } } catch (IOException ex) { log("unable to initialize formatter: " + ex.getMessage(), Project.MSG_WARN); } } if (!formattersWriteToStdOut) { checker.addDiffListener(new AntLogger(this)); } checker.addDiffListener(counter); try { ClassFilter classSelector = buildClassFilter(); final JavaType[] origClasses = BcelTypeArrayBuilder.createClassSet(origJars, origThirdPartyLoader, classSelector); final JavaType[] newClasses = BcelTypeArrayBuilder.createClassSet(newJars, newThirdPartyLoader, classSelector); checker.reportDiffs(origClasses, newClasses); } catch (CheckerException ex) { throw new BuildException(ex.getMessage()); } if ((counter.getBinWarnings() > 0 && failOnBinWarning) || (counter.getBinErrors() > 0 && failOnBinError)) { throw new BuildException("detected binary incompatible API changes"); } if ((counter.getSrcWarnings() > 0 && failOnSrcWarning) || (counter.getSrcErrors() > 0 && failOnSrcError)) { throw new BuildException("detected source incompatible API changes"); } } private ClassFilter buildClassFilter() { final PatternSetFilter patternSetFilter = new PatternSetFilter(getProject(), patternSets); final ClassFilter scopeSelector = new ClassSelector(ClassSelector.MODE_UNLESS); return new CompoundClassFilter(patternSetFilter, scopeSelector); } private ClassLoader createClasspathLoader(Path classpath) { final String[] cpEntries = classpath.list(); return ClassLoaderUtil.createClassLoader(cpEntries); } private File[] scanFileSet(FileSet fs) { Project prj = getProject(); DirectoryScanner scanner = fs.getDirectoryScanner(prj); scanner.scan(); File basedir = scanner.getBasedir(); String[] fileNames = scanner.getIncludedFiles(); File[] ret = new File[fileNames.length]; for (int i = 0; i < fileNames.length; i++) { String fileName = fileNames[i]; ret[i] = new File(basedir, fileName); } return ret; } } ./clirr-0.6/core/src/java/net/sf/clirr/ant/PatternSetFilter.java0000644000175000017500000001012010316146510024411 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.ant; import net.sf.clirr.core.ClassFilter; import java.util.List; import java.io.File; import org.apache.bcel.classfile.JavaClass; import org.apache.tools.ant.types.selectors.SelectorUtils; import org.apache.tools.ant.types.PatternSet; import org.apache.tools.ant.Project; /** * A ClassFilter that uses Ant PatternSets as the decision criteria. * * @author lkuehne */ class PatternSetFilter implements ClassFilter { private final Project project; private final List patternSets; /** * Creates a new PatternSetFilter. * @param project the current Ant project * @param patternSets a List of Ant PatternSet objects */ public PatternSetFilter(Project project, List patternSets) { this.project = project; this.patternSets = patternSets; } public boolean isSelected(JavaClass clazz) { // The patternset evaluation code below was copied from Apache Ant's Expand task. // I feel this code should be available as a library function inside Ant somewhere... String className = clazz.getClassName(); String name = className.replace('.', File.separatorChar); if (patternSets == null || patternSets.isEmpty()) { return true; } boolean included = false; for (int i = 0; i < patternSets.size(); i++) { PatternSet p = (PatternSet) patternSets.get(i); p.getIncludePatterns(project); String[] incls = p.getIncludePatterns(project); if (incls == null || incls.length == 0) { // no include pattern implicitly means includes="**" incls = new String[] {"**"}; } for (int w = 0; w < incls.length; w++) { String pattern = incls[w].replace('/', File.separatorChar) .replace('\\', File.separatorChar); if (pattern.endsWith(File.separator)) { pattern += "**"; } included = SelectorUtils.matchPath(pattern, name); if (included) { break; } } if (!included) { break; } String[] excls = p.getExcludePatterns(project); if (excls != null) { for (int w = 0; w < excls.length; w++) { String pattern = excls[w] .replace('/', File.separatorChar) .replace('\\', File.separatorChar); if (pattern.endsWith(File.separator)) { pattern += "**"; } included = !(SelectorUtils.matchPath(pattern, name)); if (!included) { break; } } } } project.log("included " + className + " = " + included, Project.MSG_VERBOSE); return included; } } ./clirr-0.6/core/src/java/net/sf/clirr/ant/package.html0000644000175000017500000000007310316146510022576 0ustar twernertwerner The Ant task implementation. ./clirr-0.6/core/src/java/net/sf/clirr/ant/ChangeCounter.java0000644000175000017500000000521710316146510023712 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.ant; import net.sf.clirr.core.ApiDifference; import net.sf.clirr.core.Severity; import net.sf.clirr.core.DiffListenerAdapter; final class ChangeCounter extends DiffListenerAdapter { private int binInfos = 0; private int binWarnings = 0; private int binErrors = 0; private int srcInfos = 0; private int srcWarnings = 0; private int srcErrors = 0; public ChangeCounter() { } public int getBinInfos() { return binInfos; } public int getBinWarnings() { return binWarnings; } public int getBinErrors() { return binErrors; } public int getSrcInfos() { return srcInfos; } public int getSrcWarnings() { return srcWarnings; } public int getSrcErrors() { return srcErrors; } public void reportDiff(ApiDifference difference) { final Severity binSeverity = difference.getBinaryCompatibilitySeverity(); if (Severity.ERROR.equals(binSeverity)) { binErrors += 1; } else if (Severity.WARNING.equals(binSeverity)) { binWarnings += 1; } else if (Severity.INFO.equals(binSeverity)) { binInfos += 1; } final Severity srcSeverity = difference.getSourceCompatibilitySeverity(); if (Severity.ERROR.equals(srcSeverity)) { srcErrors += 1; } else if (Severity.WARNING.equals(srcSeverity)) { srcWarnings += 1; } else if (Severity.INFO.equals(srcSeverity)) { srcInfos += 1; } } } ./clirr-0.6/core/src/java/net/sf/clirr/core/0000755000175000017500000000000010316146510020463 5ustar twernertwerner./clirr-0.6/core/src/java/net/sf/clirr/core/ClassFilter.java0000644000175000017500000000321210316146510023537 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core; import org.apache.bcel.classfile.JavaClass; /** * A filter for Java classes. *

* Instances of this interface are passed to the * {@link Checker#reportDiffs} method of the {@link Checker} class. *

* * @see Checker#reportDiffs * @see java.io.FileFilter * @author lkuehne */ public interface ClassFilter { /** * Tests whether or not a given java class is considered when reporting the API * differences between jar files. * * @param clazz a Java class * @return true if clazz should be considered by the Checker * in this object. */ boolean isSelected(JavaClass clazz); } ./clirr-0.6/core/src/java/net/sf/clirr/core/PlainDiffListener.java0000644000175000017500000000343010316146510024670 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core; import java.io.IOException; import java.io.PrintStream; public final class PlainDiffListener extends FileDiffListener { private MessageTranslator translator = new MessageTranslator(); public PlainDiffListener(String outFile) throws IOException { super(outFile); } public void reportDiff(ApiDifference difference) { PrintStream out = getOutputStream(); out.print(difference.getMaximumSeverity().toString()); Message m = difference.getMessage(); if (m != null) { out.print(": "); out.print(m.getId()); } out.print(": "); out.print(difference.getAffectedClass()); out.print(": "); out.println(difference.getReport(translator)); } } ./clirr-0.6/core/src/java/net/sf/clirr/core/Message.java0000644000175000017500000000454610316146510022723 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core; /** * Class which manages API Difference messages, including expanding message * codes into strings and descriptions. */ public final class Message { private int id; /** * This constructor is equivalent to new Message(id, true). */ public Message(int id) { this(id, true); } /** * Create an instance of this object with the specified message id * * @param id is an integer which is used to look up the appropriate * text string for this message from a resource file. The id of a * message should be unique. * * @param register determines whether the new Message object should be * registered with the central MessageManager object. This is normally * desirable, as this allows the unit tests associated with clirr to * verify that message ids are unique and that translations exist for * all registered messages. However false can be useful in some * circumstances, eg when creating Message objects for the purposes * of unit tests. */ public Message(int id, boolean register) { this.id = id; if (register) { MessageManager.getInstance().addMessage(this); } } public int getId() { return id; } public String toString() { return "Message#" + id; } } ./clirr-0.6/core/src/java/net/sf/clirr/core/Severity.java0000644000175000017500000000400610316146510023140 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core; /** * Enumeration type for severity of an Api difference. * * @author lkuehne */ public final class Severity implements Comparable { private String representation; private int value; private Severity(String representation, int value) { this.representation = representation; this.value = value; } /** marks an api difference that is binary compatible. */ public static final Severity INFO = new Severity("INFO", 0); /** marks an api difference that might be binary incompatible. */ public static final Severity WARNING = new Severity("WARNING", 1); /** marks an api difference that is binary incompatible. */ public static final Severity ERROR = new Severity("ERROR", 2); /** @see java.lang.Object#toString() */ public String toString() { return representation; } /** {@inheritDoc} */ public int compareTo(Object o) { Severity other = (Severity) o; return this.value - other.value; } } ./clirr-0.6/core/src/java/net/sf/clirr/core/internal/0000755000175000017500000000000010316146510022277 5ustar twernertwerner./clirr-0.6/core/src/java/net/sf/clirr/core/internal/checks/0000755000175000017500000000000010316146510023537 5ustar twernertwerner./clirr-0.6/core/src/java/net/sf/clirr/core/internal/checks/FieldSetCheck.java0000644000175000017500000002366110316146510027047 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core.internal.checks; import java.util.Comparator; import net.sf.clirr.core.internal.ClassChangeCheck; import net.sf.clirr.core.internal.AbstractDiffReporter; import net.sf.clirr.core.internal.ApiDiffDispatcher; import net.sf.clirr.core.internal.CoIterator; import net.sf.clirr.core.internal.NameComparator; import net.sf.clirr.core.spi.Field; import net.sf.clirr.core.spi.JavaType; import net.sf.clirr.core.spi.Scope; import net.sf.clirr.core.ApiDifference; import net.sf.clirr.core.Severity; import net.sf.clirr.core.ScopeSelector; import net.sf.clirr.core.Message; /** * Checks the fields of a class. * * @author lkuehne */ public class FieldSetCheck extends AbstractDiffReporter implements ClassChangeCheck { private static final Message MSG_FIELD_ADDED = new Message(6000); private static final Message MSG_FIELD_REMOVED = new Message(6001); private static final Message MSG_FIELD_NOT_CONSTANT = new Message(6002); private static final Message MSG_FIELD_CONSTANT_CHANGED = new Message(6003); private static final Message MSG_FIELD_TYPE_CHANGED = new Message(6004); private static final Message MSG_FIELD_NOW_NON_FINAL = new Message(6005); private static final Message MSG_FIELD_NOW_FINAL = new Message(6006); private static final Message MSG_FIELD_NOW_NON_STATIC = new Message(6007); private static final Message MSG_FIELD_NOW_STATIC = new Message(6008); private static final Message MSG_FIELD_MORE_ACCESSIBLE = new Message(6009); private static final Message MSG_FIELD_LESS_ACCESSIBLE = new Message(6010); private static final Message MSG_CONSTANT_FIELD_REMOVED = new Message(6011); private static final Comparator COMPARATOR = new NameComparator(); private ScopeSelector scopeSelector; public FieldSetCheck(ApiDiffDispatcher dispatcher, ScopeSelector scopeSelector) { super(dispatcher); this.scopeSelector = scopeSelector; } public final boolean check(JavaType baselineClass, JavaType currentClass) { final Field[] baselineFields = baselineClass.getFields(); final Field[] currentFields = currentClass.getFields(); CoIterator iter = new CoIterator( COMPARATOR, baselineFields, currentFields); while (iter.hasNext()) { iter.next(); Field bField = (Field) iter.getLeft(); Field cField = (Field) iter.getRight(); if (bField == null) { if (scopeSelector.isSelected(cField)) { String scope = cField.getDeclaredScope().getDesc(); fireDiff(MSG_FIELD_ADDED, Severity.INFO, currentClass, cField, new String[]{scope}); } } else if (cField == null) { if (scopeSelector.isSelected(bField)) { if ((bField.getConstantValue() != null) && bField.isFinal()) { // Fields which are compile-time constants will have // been inlined into callers; even though the field // has been deleted, the caller will continue to use // the old value. The result is therefore not // technically a binary incompatibility, though it is // a source-code incompatibility. // See bugtracker #961222 fireDiff(MSG_CONSTANT_FIELD_REMOVED, getSeverity(baselineClass, bField, Severity.WARNING), getSeverity(baselineClass, bField, Severity.ERROR), baselineClass, bField, null); } else { fireDiff(MSG_FIELD_REMOVED, getSeverity(baselineClass, bField, Severity.ERROR), baselineClass, bField, null); } } } else if (scopeSelector.isSelected(bField) || scopeSelector.isSelected(cField)) { checkForModifierChange(bField, cField, currentClass); checkForVisibilityChange(bField, cField, currentClass); checkForTypeChange(bField, cField, currentClass); checkForConstantValueChange(bField, cField, currentClass); } } return true; } private void checkForConstantValueChange(Field bField, Field cField, JavaType currentClass) { if (!(bField.isStatic() && bField.isFinal() && cField.isStatic() && cField.isFinal())) { return; } final Object bVal = bField.getConstantValue(); if (bVal != null) { final String bValRep = bVal.toString(); final Object cVal = cField.getConstantValue(); if (cVal == null) { // TODO: also check whether old field is final. If it's not // final, then external code cannot have inlined the // constant, and therefore we can issue an INFO instead // of a warning. Actually, may be better to introduce a // different message code rather than issue this code with // two different severity levels.. fireDiff(MSG_FIELD_NOT_CONSTANT, getSeverity(currentClass, bField, Severity.WARNING), currentClass, cField, null); return; } final String cValRep = String.valueOf(cVal); if (!bValRep.equals(cValRep)) { // TODO: print out old and new value // How can that be done with BCEL, esp. for boolean values? // // TODO: also check whether field is final (see above). fireDiff(MSG_FIELD_CONSTANT_CHANGED, getSeverity(currentClass, bField, Severity.WARNING), currentClass, cField, null); } } } private void checkForTypeChange(Field bField, Field cField, JavaType currentClass) { final String bSig = bField.getType().toString(); final String cSig = cField.getType().toString(); if (!bSig.equals(cSig)) { fireDiff(MSG_FIELD_TYPE_CHANGED, getSeverity(currentClass, bField, Severity.ERROR), currentClass, bField, new String[] {bSig, cSig}); } } private void checkForModifierChange(Field bField, Field cField, JavaType clazz) { if (bField.isFinal() && !cField.isFinal()) { fireDiff(MSG_FIELD_NOW_NON_FINAL, Severity.INFO, clazz, cField, null); } if (!bField.isFinal() && cField.isFinal()) { fireDiff(MSG_FIELD_NOW_FINAL, Severity.ERROR, clazz, cField, null); } if (bField.isStatic() && !cField.isStatic()) { fireDiff(MSG_FIELD_NOW_NON_STATIC, getSeverity(clazz, bField, Severity.ERROR), clazz, cField, null); } if (!bField.isStatic() && cField.isStatic()) { fireDiff(MSG_FIELD_NOW_STATIC, getSeverity(clazz, bField, Severity.ERROR), clazz, cField, null); } // JLS, 13.4.10: Adding or deleting a transient modifier of a field // does not break compatibility with pre-existing binaries // TODO: What about volatile? } private void checkForVisibilityChange(Field bField, Field cField, JavaType clazz) { Scope bScope = bField.getEffectiveScope(); Scope cScope = cField.getEffectiveScope(); if (cScope.isMoreVisibleThan(bScope)) { fireDiff(MSG_FIELD_MORE_ACCESSIBLE, Severity.INFO, clazz, cField, new String[] {bScope.getDesc(), cScope.getDesc()}); } else if (cScope.isLessVisibleThan(bScope)) { fireDiff(MSG_FIELD_LESS_ACCESSIBLE, getSeverity(clazz, bField, Severity.ERROR), clazz, cField, new String[] {bScope.getDesc(), cScope.getDesc()}); } } private void fireDiff( Message msg, Severity severity, JavaType clazz, Field field, String[] args) { fireDiff(msg, severity, severity, clazz, field, args); } private void fireDiff( Message msg, Severity binarySeverity, Severity sourceSeverity, JavaType clazz, Field field, String[] args) { final String className = clazz.getName(); final ApiDifference diff = new ApiDifference( msg, binarySeverity, sourceSeverity, className, null, field.getName(), args); getApiDiffDispatcher().fireDiff(diff); } } ./clirr-0.6/core/src/java/net/sf/clirr/core/internal/checks/InterfaceSetCheck.java0000644000175000017500000001176010316146510027721 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core.internal.checks; import java.util.Set; import java.util.TreeSet; import net.sf.clirr.core.Severity; import net.sf.clirr.core.Message; import net.sf.clirr.core.internal.AbstractDiffReporter; import net.sf.clirr.core.internal.ApiDiffDispatcher; import net.sf.clirr.core.internal.ClassChangeCheck; import net.sf.clirr.core.internal.CoIterator; import net.sf.clirr.core.internal.NameComparator; import net.sf.clirr.core.spi.JavaType; /** * Detects changes in the set of interfaces implemented by a class. * * @author lkuehne */ public final class InterfaceSetCheck extends AbstractDiffReporter implements ClassChangeCheck { private static final Message MSG_IFACE_ADDED = new Message(4000); private static final Message MSG_IFACE_REMOVED = new Message(4001); /** * Create a new instance of this check. * @param dispatcher the diff dispatcher that distributes the detected changes to the listeners. */ public InterfaceSetCheck(ApiDiffDispatcher dispatcher) { super(dispatcher); } /** {@inheritDoc} */ public boolean check(JavaType compatBaseline, JavaType currentVersion) { JavaType[] compatInterfaces = compatBaseline.getAllInterfaces(); JavaType[] currentInterfaces = currentVersion.getAllInterfaces(); // Note: getAllInterfaces might return multiple array entries with the same // interface, so we need to use sets to remove duplicates... Set compat = createClassSet(compatInterfaces); Set current = createClassSet(currentInterfaces); final String className = compatBaseline.getName(); CoIterator iter = new CoIterator( new NameComparator(), compat, current); while (iter.hasNext()) { iter.next(); JavaType compatInterface = (JavaType) iter.getLeft(); JavaType currentInterface = (JavaType) iter.getRight(); if (compatInterface != null && className.equals(compatInterface.getName()) || currentInterface != null && className.equals(currentInterface.getName())) { // This occurs because an interface has itself in the set of all interfaces. // We can't just let the test below handle this case because that won't // work when a gender change has occurred. continue; } if (compatInterface == null) { // TODO: check whether the class already implements // throwable. If so, this should probably be a warning, // because the presence of this extra interface could // change exception-catching behaviour. // // Actually, it could also change code which uses // "instance-of" and similar methods too, even when not // a throwable. However this is fairly low probability.. log(MSG_IFACE_ADDED, Severity.INFO, className, null, null, new String[] {currentInterface.getName()}); } else if (currentInterface == null) { log(MSG_IFACE_REMOVED, getSeverity(compatBaseline, Severity.ERROR), className, null, null, new String[] {compatInterface.getName()}); } } return true; } /** * Creates a Set of JavaType objects. * @param classes the classes to include in the set, might contain duplicates * @return Set */ private Set createClassSet(JavaType[] classes) { // JavaType does not specify the semantics of equals(), so we use a Set implementation // that determines equality by invoking a Comparator instead of calling equals() Set current = new TreeSet(new NameComparator()); for (int i = 0; i < classes.length; i++) { current.add(classes[i]); } return current; } } ./clirr-0.6/core/src/java/net/sf/clirr/core/internal/checks/package.html0000644000175000017500000000013110316146510026013 0ustar twernertwerner The check modules that implement the funtionality of clirr. ./clirr-0.6/core/src/java/net/sf/clirr/core/internal/checks/ClassModifierCheck.java0000644000175000017500000001261210316146510030066 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core.internal.checks; import net.sf.clirr.core.Severity; import net.sf.clirr.core.Message; import net.sf.clirr.core.internal.AbstractDiffReporter; import net.sf.clirr.core.internal.ApiDiffDispatcher; import net.sf.clirr.core.internal.ClassChangeCheck; import net.sf.clirr.core.spi.JavaType; import net.sf.clirr.core.spi.Method; import net.sf.clirr.core.spi.Scope; /** * Detects changes in class modifiers (abstract, final). * * @author lkuehne */ public final class ClassModifierCheck extends AbstractDiffReporter implements ClassChangeCheck { private static final Message MSG_MODIFIER_UNABLE_TO_DETERMINE_CLASS_SCOPE = new Message(3000); private static final Message MSG_MODIFIER_REMOVED_FINAL = new Message(3001); private static final Message MSG_MODIFIER_ADDED_FINAL_TO_EFFECTIVE_FINAL = new Message(3002); private static final Message MSG_MODIFIER_ADDED_FINAL = new Message(3003); private static final Message MSG_MODIFIER_REMOVED_ABSTRACT = new Message(3004); private static final Message MSG_MODIFIER_ADDED_ABSTRACT = new Message(3005); /** * Create a new instance of this check. * @param dispatcher the diff dispatcher that distributes the detected changes to the listeners. */ public ClassModifierCheck(ApiDiffDispatcher dispatcher) { super(dispatcher); } /** {@inheritDoc} */ public boolean check(JavaType compatBaseLine, JavaType currentVersion) { final String className = compatBaseLine.getName(); Scope currentScope = currentVersion.getEffectiveScope(); if (currentScope.isLessVisibleThan(Scope.PACKAGE)) { // for private classes, we don't care if they are now final, // or now abstract, or now an interface. return true; } final boolean currentIsFinal = currentVersion.isFinal(); final boolean compatIsFinal = compatBaseLine.isFinal(); final boolean currentIsAbstract = currentVersion.isAbstract(); final boolean compatIsAbstract = compatBaseLine.isAbstract(); final boolean currentIsInterface = currentVersion.isInterface(); final boolean compatIsInterface = compatBaseLine.isInterface(); if (compatIsFinal && !currentIsFinal) { log(MSG_MODIFIER_REMOVED_FINAL, Severity.INFO, className, null, null, null); } else if (!compatIsFinal && currentIsFinal) { if (isEffectivelyFinal(compatBaseLine)) { log(MSG_MODIFIER_ADDED_FINAL_TO_EFFECTIVE_FINAL, Severity.INFO, className, null, null, null); } else { log(MSG_MODIFIER_ADDED_FINAL, getSeverity(compatBaseLine, Severity.ERROR), className, null, null, null); } } // interfaces are always abstract, don't report gender change here if (compatIsAbstract && !currentIsAbstract && !compatIsInterface) { log(MSG_MODIFIER_REMOVED_ABSTRACT, Severity.INFO, className, null, null, null); } else if (!compatIsAbstract && currentIsAbstract && !currentIsInterface) { log(MSG_MODIFIER_ADDED_ABSTRACT, getSeverity(compatBaseLine, Severity.ERROR), className, null, null, null); } return true; } /** * There are cases where nonfinal classes are effectively final * because they do not have public or protected ctors. For such * classes we should not emit errors when a final modifier is * introduced. */ private boolean isEffectivelyFinal(JavaType clazz) { if (clazz.isFinal()) { return true; } // iterate over all constructors, and detect whether any are // public or protected. If so, return false. Method[] methods = clazz.getMethods(); for (int i = 0; i < methods.length; ++i) { Method method = methods[i]; final String methodName = method.getName(); if (methodName.equals("")) { if (method.getEffectiveScope().isMoreVisibleThan(Scope.PACKAGE)) { return false; } } } // no public or protected constructor found return true; } } ./clirr-0.6/core/src/java/net/sf/clirr/core/internal/checks/GenderChangeCheck.java0000644000175000017500000000501110316146510027647 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core.internal.checks; import net.sf.clirr.core.Severity; import net.sf.clirr.core.Message; import net.sf.clirr.core.internal.AbstractDiffReporter; import net.sf.clirr.core.internal.ApiDiffDispatcher; import net.sf.clirr.core.internal.ClassChangeCheck; import net.sf.clirr.core.spi.JavaType; /** * Detects gender changes (a class became an interface or vice versa). * * @author lkuehne */ public final class GenderChangeCheck extends AbstractDiffReporter implements ClassChangeCheck { private static final Message MSG_GENDER_CLASS_TO_INTERFACE = new Message(2000); private static final Message MSG_GENDER_INTERFACE_TO_CLASS = new Message(2001); /** * Create a new instance of this check. * @param dispatcher the diff dispatcher that distributes the detected changes to the listeners. */ public GenderChangeCheck(ApiDiffDispatcher dispatcher) { super(dispatcher); } /** {@inheritDoc} */ public boolean check(JavaType baseLine, JavaType current) { if (!baseLine.isInterface() && current.isInterface()) { log(MSG_GENDER_CLASS_TO_INTERFACE, getSeverity(baseLine, Severity.ERROR), baseLine.getName(), null, null, null); } else if (baseLine.isInterface() && !current.isInterface()) { log(MSG_GENDER_INTERFACE_TO_CLASS, getSeverity(baseLine, Severity.ERROR), baseLine.getName(), null, null, null); } return true; } } ./clirr-0.6/core/src/java/net/sf/clirr/core/internal/checks/ClassScopeCheck.java0000644000175000017500000001131010316146510027373 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core.internal.checks; import net.sf.clirr.core.Severity; import net.sf.clirr.core.ScopeSelector; import net.sf.clirr.core.Message; import net.sf.clirr.core.internal.AbstractDiffReporter; import net.sf.clirr.core.internal.ApiDiffDispatcher; import net.sf.clirr.core.internal.ClassChangeCheck; import net.sf.clirr.core.spi.JavaType; import net.sf.clirr.core.spi.Scope; /** * Detects changes in class access declaration, for both "top-level" classes, * and nested classes. *

* Java class files only ever contain scope specifiers of "public" or "package". * For top-level classes, this is expected: it is not possible to have a * top-level protected or private class. *

* However nested classes can be declared as protected or private. The * way to tell the real scope of a nested class is to ignore the scope in * the actual class file itself, and instead look in the "InnerClasses" * attribute stored on the enclosing class. This is exactly what the java * compiler does when compiling, and what the jvm does when verifying class * linkage at runtime. * * @author Simon Kitching */ public final class ClassScopeCheck extends AbstractDiffReporter implements ClassChangeCheck { private static final Message MSG_SCOPE_INCREASED = new Message(1000); private static final Message MSG_SCOPE_DECREASED = new Message(1001); private static final Message MSG_ERROR_DETERMINING_SCOPE_OLD = new Message(1002); private static final Message MSG_ERROR_DETERMINING_SCOPE_NEW = new Message(1003); private ScopeSelector scopeSelector; /** * Create a new instance of this check. * @param dispatcher the diff dispatcher that distributes the detected changes to the listeners. */ public ClassScopeCheck(ApiDiffDispatcher dispatcher, ScopeSelector scopeSelector) { super(dispatcher); this.scopeSelector = scopeSelector; } /** {@inheritDoc} */ public boolean check(JavaType compatBaseline, JavaType currentVersion) { Scope bScope = compatBaseline.getEffectiveScope(); Scope cScope = currentVersion.getEffectiveScope(); if (!scopeSelector.isSelected(bScope) && !scopeSelector.isSelected(cScope)) { // neither the old nor the new class are "visible" at the scope // the user of this class cares about, so just skip this test // and all following tests for this pair of classes. return false; } if (cScope.isMoreVisibleThan(bScope)) { String[] args = {bScope.getDesc(), cScope.getDesc()}; log(MSG_SCOPE_INCREASED, Severity.INFO, compatBaseline.getName(), null, null, args); } else if (cScope.isLessVisibleThan(bScope)) { String[] args = {bScope.getDesc(), cScope.getDesc()}; log(MSG_SCOPE_DECREASED, getSeverity(compatBaseline, Severity.ERROR), compatBaseline.getName(), null, null, args); } // Apply further checks only if both versions of the class have scopes // of interest. For example, when the user is only interested in // public & protected classes, then for classes which have just become // public/protected we just want to report that it is now "visible"; // because the class was not visible before the differences since its // last version are not relevant. And for classes which are no longer // public/protected, we just want to report that the whole class is no // longer "visible"; as it is not visible to users any changes to it // are irrelevant. return scopeSelector.isSelected(bScope) && scopeSelector.isSelected(cScope); } } ./clirr-0.6/core/src/java/net/sf/clirr/core/internal/checks/ClassHierarchyCheck.java0000644000175000017500000000760510316146510030254 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core.internal.checks; import net.sf.clirr.core.Message; import net.sf.clirr.core.Severity; import net.sf.clirr.core.internal.AbstractDiffReporter; import net.sf.clirr.core.internal.ApiDiffDispatcher; import net.sf.clirr.core.internal.ClassChangeCheck; import net.sf.clirr.core.internal.CoIterator; import net.sf.clirr.core.internal.NameComparator; import net.sf.clirr.core.spi.JavaType; /** * Detects changes in the set of superclasses. * * @author lkuehne */ public final class ClassHierarchyCheck extends AbstractDiffReporter implements ClassChangeCheck { private static final Message MSG_ADDED_CLASS_TO_SUPERCLASSES = new Message(5000); private static final Message MSG_REMOVED_CLASS_FROM_SUPERCLASSES = new Message(5001); /** * Create a new instance of this check. * @param dispatcher the diff dispatcher that distributes the detected changes to the listeners. */ public ClassHierarchyCheck(ApiDiffDispatcher dispatcher) { super(dispatcher); } /** {@inheritDoc} */ public boolean check(JavaType compatBaseline, JavaType currentVersion) { JavaType[] compatSupers = compatBaseline.getSuperClasses(); JavaType[] currentSupers = currentVersion.getSuperClasses(); boolean isThrowable = false; for (int i = 0; i < compatSupers.length; i++) { JavaType javaClass = compatSupers[i]; if ("java.lang.Throwable".equals(javaClass.getName())) { isThrowable = true; } } final String className = compatBaseline.getName(); CoIterator iter = new CoIterator(new NameComparator(), compatSupers, currentSupers); while (iter.hasNext()) { iter.next(); JavaType baselineSuper = (JavaType) iter.getLeft(); JavaType currentSuper = (JavaType) iter.getRight(); if (baselineSuper == null) { Severity severity; if (isThrowable) { // report a warning, because a change to the set of types // implemented by a thrown object can affect the // exception-catching behaviour of a program. severity = Severity.WARNING; } else { severity = Severity.INFO; } log(MSG_ADDED_CLASS_TO_SUPERCLASSES, getSeverity(compatBaseline, severity), className, null, null, new String[] {currentSuper.getName()}); } else if (currentSuper == null) { log(MSG_REMOVED_CLASS_FROM_SUPERCLASSES, getSeverity(compatBaseline, Severity.ERROR), className, null, null, new String[] {baselineSuper.getName()}); } } return true; } } ./clirr-0.6/core/src/java/net/sf/clirr/core/internal/checks/MethodSetCheck.java0000644000175000017500000006717710316146510027256 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core.internal.checks; import net.sf.clirr.core.ApiDifference; import net.sf.clirr.core.Message; import net.sf.clirr.core.Severity; import net.sf.clirr.core.ScopeSelector; import net.sf.clirr.core.internal.AbstractDiffReporter; import net.sf.clirr.core.internal.ApiDiffDispatcher; import net.sf.clirr.core.internal.ClassChangeCheck; import net.sf.clirr.core.internal.CoIterator; import net.sf.clirr.core.spi.JavaType; import net.sf.clirr.core.spi.Method; import net.sf.clirr.core.spi.Scope; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Checks the methods of a class. * * @author lkuehne */ public class MethodSetCheck extends AbstractDiffReporter implements ClassChangeCheck { private static final Message MSG_METHOD_NOW_IN_SUPERCLASS = new Message(7000); private static final Message MSG_METHOD_NOW_IN_INTERFACE = new Message(7001); private static final Message MSG_METHOD_REMOVED = new Message(7002); private static final Message MSG_METHOD_OVERRIDE_REMOVED = new Message(7003); private static final Message MSG_METHOD_ARGCOUNT_CHANGED = new Message(7004); private static final Message MSG_METHOD_PARAMTYPE_CHANGED = new Message(7005); private static final Message MSG_METHOD_RETURNTYPE_CHANGED = new Message(7006); private static final Message MSG_METHOD_DEPRECATED = new Message(7007); private static final Message MSG_METHOD_UNDEPRECATED = new Message(7008); private static final Message MSG_METHOD_LESS_ACCESSIBLE = new Message(7009); private static final Message MSG_METHOD_MORE_ACCESSIBLE = new Message(7010); private static final Message MSG_METHOD_ADDED = new Message(7011); private static final Message MSG_METHOD_ADDED_TO_INTERFACE = new Message(7012); private static final Message MSG_ABSTRACT_METHOD_ADDED = new Message(7013); private static final Message MSG_METHOD_NOW_FINAL = new Message(7014); private static final Message MSG_METHOD_NOW_NONFINAL = new Message(7015); private ScopeSelector scopeSelector; /** * Instantiates the check. * * @param dispatcher the dispatcher where detected differences shoudl be reported. * @param scopeSelector defines the scopes to look at when searching for differences. */ public MethodSetCheck(ApiDiffDispatcher dispatcher, ScopeSelector scopeSelector) { super(dispatcher); this.scopeSelector = scopeSelector; } public final boolean check(JavaType compatBaseline, JavaType currentVersion) { // Dont't report method problems when gender has changed, as // really the whole API is a pile of crap then - let GenderChange check // do it's job, and that's it if (compatBaseline.isInterface() ^ currentVersion.isInterface()) { return true; } Map bNameToMethod = buildNameToMethodMap(compatBaseline); Map cNameToMethod = buildNameToMethodMap(currentVersion); CoIterator iter = new CoIterator(null, bNameToMethod.keySet(), cNameToMethod.keySet()); while (iter.hasNext()) { iter.next(); String baselineMethodName = (String) iter.getLeft(); String currentMethodName = (String) iter.getRight(); if (baselineMethodName == null) { // a new method name has been added in the new version List currentMethods = (List) cNameToMethod.get(currentMethodName); reportMethodsAdded(currentVersion, currentMethods); } else if (currentMethodName == null) { // all methods with name x have been removed from the old version List baselineMethods = (List) bNameToMethod.get(baselineMethodName); reportMethodsRemoved(compatBaseline, baselineMethods, currentVersion); } else { // assert baselineMethodName equals currentMethodName List baselineMethods = (List) bNameToMethod.get(baselineMethodName); List currentMethods = (List) cNameToMethod.get(currentMethodName); filterSoftMatchedMethods( compatBaseline, baselineMethods, currentVersion, currentMethods); filterChangedMethods( baselineMethodName, compatBaseline, baselineMethods, currentVersion, currentMethods); // if any methods are left, they have no matching method in // the other version, so report as removed or added respectively. if (!baselineMethods.isEmpty()) { reportMethodsRemoved(compatBaseline, baselineMethods, currentVersion); } if (!currentMethods.isEmpty()) { reportMethodsAdded(currentVersion, currentMethods); } } } return true; } /** * Given a list of old and new methods for a particular method name, * find the (old, new) method pairs which have identical argument lists. *

* For these: *

    *
  • report on changes in accessibility, return type, etc *
  • remove from the list *
* * On return from this method, the old and new method lists contain only * methods whose argument lists have changed between versions [or possibly, * methods which have been deleted while one or more new methods of the * same name have been added, depending on how you view it]. All other * situations have been dealt with. *

* Note that one or both method lists may be empty on return from * this method. */ private void filterSoftMatchedMethods( JavaType compatBaseline, List baselineMethods, JavaType currentVersion, List currentMethods) { for (Iterator bIter = baselineMethods.iterator(); bIter.hasNext();) { Method bMethod = (Method) bIter.next(); for (Iterator cIter = currentMethods.iterator(); cIter.hasNext();) { Method cMethod = (Method) cIter.next(); if (isSoftMatch(bMethod, cMethod)) { check(compatBaseline, bMethod, cMethod); bIter.remove(); cIter.remove(); break; } } } } /** * Two methods are a "soft" match if they have the same name and argument * list. No two methods on the same class are ever a "soft match" for * each other, because the compiler requires distinct parameter lists for * overloaded methods. This also implies that for a given method on an "old" * class version, there are either zero or one "soft matches" on the new * version. *

* However a "soft match" is not sufficient to ensure binary compatibility. * A change in the method return type will result in a link error when used * with code compiled against the previous version of the class. *

* There may also be other differences between methods that are regarded * as "soft matches": the exceptions thrown, the deprecation status of the * methods, their accessibility, etc. */ private boolean isSoftMatch(Method oldMethod, Method newMethod) { String oldName = oldMethod.getName(); String newName = newMethod.getName(); if (!oldName.equals(newName)) { return false; } StringBuffer buf = new StringBuffer(); appendHumanReadableArgTypeList(oldMethod, buf); String oldArgs = buf.toString(); buf.setLength(0); appendHumanReadableArgTypeList(newMethod, buf); String newArgs = buf.toString(); return (oldArgs.equals(newArgs)); } /** * For each method in the baselineMethods list, find the "best match" * in the currentMethods list, report the changes between this method * pair, then remove both methods from the lists. *

* On return, at least one of the method lists will be empty. */ private void filterChangedMethods( String methodName, JavaType compatBaseline, List baselineMethods, JavaType currentVersion, List currentMethods) { // ok, we now have to deal with the tricky cases, where it is not // immediately obvious which old methods correspond to which new // methods. // // Here we build a similarity table, i.e. for new method i and old // method j we have number that charaterizes how similar the method // signatures are (0 means equal, higher number means more different) while (!baselineMethods.isEmpty() && !currentMethods.isEmpty()) { int[][] similarityTable = buildSimilarityTable(baselineMethods, currentMethods); int min = Integer.MAX_VALUE; int iMin = baselineMethods.size(); int jMin = currentMethods.size(); for (int i = 0; i < baselineMethods.size(); i++) { for (int j = 0; j < currentMethods.size(); j++) { final int tableEntry = similarityTable[i][j]; if (tableEntry < min) { min = tableEntry; iMin = i; jMin = j; } } } Method iMethod = (Method) baselineMethods.remove(iMin); Method jMethod = (Method) currentMethods.remove(jMin); check(compatBaseline, iMethod, jMethod); } } private int[][] buildSimilarityTable(List baselineMethods, List currentMethods) { int[][] similarityTable = new int[baselineMethods.size()][currentMethods.size()]; for (int i = 0; i < baselineMethods.size(); i++) { for (int j = 0; j < currentMethods.size(); j++) { final Method iMethod = (Method) baselineMethods.get(i); final Method jMethod = (Method) currentMethods.get(j); similarityTable[i][j] = distance(iMethod, jMethod); } } return similarityTable; } private int distance(Method m1, Method m2) { final JavaType[] m1Args = m1.getArgumentTypes(); final JavaType[] m2Args = m2.getArgumentTypes(); if (m1Args.length != m2Args.length) { return 1000 * Math.abs(m1Args.length - m2Args.length); } int retVal = 0; for (int i = 0; i < m1Args.length; i++) { if (!m1Args[i].toString().equals(m2Args[i].toString())) { retVal += 1; } } return retVal; } /** * Searches the class hierarchy for a method that has a certain signature. * @param methodSignature the sig we're looking for * @param clazz class where search starts * @return class name of a superclass of clazz, might be null */ private String findSuperClassWithSignature(String methodSignature, JavaType clazz) { final JavaType[] superClasses = clazz.getSuperClasses(); for (int i = 0; i < superClasses.length; i++) { JavaType superClass = superClasses[i]; final Method[] superMethods = superClass.getMethods(); for (int j = 0; j < superMethods.length; j++) { Method superMethod = superMethods[j]; final String superMethodSignature = getMethodId(superClass, superMethod); if (methodSignature.equals(superMethodSignature)) { return superClass.getName(); } } } return null; } /** * Searches the class hierarchy for a method that has a certtain signature. * @param methodSignature the sig we're looking for * @param clazz class where search starts * @return class name of a superinterface of clazz, might be null */ private String findSuperInterfaceWithSignature(String methodSignature, JavaType clazz) { final JavaType[] superClasses = clazz.getAllInterfaces(); for (int i = 0; i < superClasses.length; i++) { JavaType superClass = superClasses[i]; final Method[] superMethods = superClass.getMethods(); for (int j = 0; j < superMethods.length; j++) { Method superMethod = superMethods[j]; final String superMethodSignature = getMethodId(superClass, superMethod); if (methodSignature.equals(superMethodSignature)) { return superClass.getName(); } } } return null; } /** * Given a list of methods, report each one as being removed. */ private void reportMethodsRemoved( JavaType baselineClass, List baselineMethods, JavaType currentClass) { for (Iterator i = baselineMethods.iterator(); i.hasNext();) { Method method = (Method) i.next(); reportMethodRemoved(baselineClass, method, currentClass); } } /** * Report that a method has been removed from a class. * @param oldClass the class where the method was available * @param oldMethod the method that has been removed * @param currentClass the superclass where the method is now available, might be null */ private void reportMethodRemoved( JavaType oldClass, Method oldMethod, JavaType currentClass) { if (!scopeSelector.isSelected(oldMethod)) { return; } String signature = getMethodId(oldClass, oldMethod); String oldBaseClassForMethod = findSuperClassWithSignature(signature, oldClass); String oldInterfaceForMethod = findSuperInterfaceWithSignature(signature, oldClass); String newBaseClassForMethod = findSuperClassWithSignature(signature, currentClass); String newInterfaceForMethod = findSuperInterfaceWithSignature(signature, currentClass); boolean oldInheritedMethod = (oldBaseClassForMethod != null) || (oldInterfaceForMethod != null); boolean newInheritedMethod = (newBaseClassForMethod != null) || (newInterfaceForMethod != null); if (oldInheritedMethod && newInheritedMethod) { // Previously, this method overrode an inherited definition. // The current version of the class doesn't have this // method, but a parent class or interface still does, so this // does not cause an incompatibility. fireDiff(MSG_METHOD_OVERRIDE_REMOVED, Severity.INFO, oldClass, oldMethod, null); } else if (oldInheritedMethod) { // Previously, this method override an inherited definition. // It isn't present in the current class, though, and neither is // it present in the new class' ancestors. Best to just // report it as removed... fireDiff(MSG_METHOD_REMOVED, getSeverity(oldClass, oldMethod, Severity.ERROR), oldClass, oldMethod, null); } else if (newBaseClassForMethod != null) { // Previously, this method didn't override anything. The current // version of this class doesn't have this method any more, // but an ancestor class now *does*. This is an instance // of the pull-up refactoring pattern, where a method is moved // to an ancestor class. fireDiff(MSG_METHOD_NOW_IN_SUPERCLASS, Severity.INFO, oldClass, oldMethod, new String[] {newBaseClassForMethod}); } else if (newInterfaceForMethod != null) { // Previously, this method didn't override anything. The current // version of this class doesn't have this method any more, // but one of the implemented interfaces now *does*. This is an // instance of the pull-up refactoring pattern, where a method is // moved to an interface. fireDiff(MSG_METHOD_NOW_IN_INTERFACE, Severity.INFO, oldClass, oldMethod, new String[] {newInterfaceForMethod}); } else { // This method wasn't anything special in the old class, and // it isn't present in the new class either directly or via // inheritance. fireDiff(MSG_METHOD_REMOVED, getSeverity(oldClass, oldMethod, Severity.ERROR), oldClass, oldMethod, null); } } /** * Given a list of methods, report each one as being added. */ private void reportMethodsAdded( JavaType currentClass, List currentMethods) { for (Iterator i = currentMethods.iterator(); i.hasNext();) { Method method = (Method) i.next(); reportMethodAdded(currentClass, method); } } /** * Report that a method has been added to a class. */ private void reportMethodAdded(JavaType newClass, Method newMethod) { if (!scopeSelector.isSelected(newMethod)) { return; } if (newClass.isInterface()) { // TODO: this is not an incompatibility if the new method // actually already exists on a parent interface of the // old interface. In that case, any class implementing the // old version of this interface must already have an // implementation of this method. See bugtracker #961217 fireDiff(MSG_METHOD_ADDED_TO_INTERFACE, getSeverity(newClass, newMethod, Severity.ERROR), newClass, newMethod, null); } else if (newMethod.isAbstract()) { // TODO: this is not an incompatibility if the new method // actually already exists on a parent interface of the // old interface and was abstract. In that case, any class // extending the old version of this class must already // have an implementation of this method. // // Note that abstract methods can never be package or private // scope, so we don't need to use the getSeverity method. fireDiff(MSG_ABSTRACT_METHOD_ADDED, Severity.ERROR, newClass, newMethod, null); } else { // TODO: //. (a) check whether this method exists on a parent of the // new class. If so, indicate that this new method is overriding // some inherited method. // (b) if not a, then check whether this method exists on a parent // of the old class. If so, then report that the method has // been moved from the parent to the child class. This is // potentially useful info for the user. // // See bugtracker #959225 fireDiff(MSG_METHOD_ADDED, Severity.INFO, newClass, newMethod, null); } } /** * Builds a map from a method name to a List of methods. */ private Map buildNameToMethodMap(JavaType clazz) { Method[] methods = clazz.getMethods(); Map retVal = new HashMap(); for (int i = 0; i < methods.length; i++) { Method method = methods[i]; final String name = method.getName(); List set = (List) retVal.get(name); if (set == null) { set = new ArrayList(); retVal.put(name, set); } set.add(method); } return retVal; } private void check(JavaType compatBaseline, Method baselineMethod, Method currentMethod) { if (!scopeSelector.isSelected(baselineMethod) && !scopeSelector.isSelected(currentMethod)) { return; } checkParameterTypes(compatBaseline, baselineMethod, currentMethod); checkReturnType(compatBaseline, baselineMethod, currentMethod); checkDeclaredExceptions(compatBaseline, baselineMethod, currentMethod); checkDeprecated(compatBaseline, baselineMethod, currentMethod); checkVisibility(compatBaseline, baselineMethod, currentMethod); checkFinal(compatBaseline, baselineMethod, currentMethod); } private void checkParameterTypes(JavaType compatBaseline, Method baselineMethod, Method currentMethod) { JavaType[] bArgs = baselineMethod.getArgumentTypes(); JavaType[] cArgs = currentMethod.getArgumentTypes(); if (bArgs.length != cArgs.length) { fireDiff(MSG_METHOD_ARGCOUNT_CHANGED, getSeverity(compatBaseline, baselineMethod, Severity.ERROR), compatBaseline, baselineMethod, null); return; } //System.out.println("baselineMethod = " + getMethodId(compatBaseline, baselineMethod)); for (int i = 0; i < bArgs.length; i++) { JavaType bArg = bArgs[i]; JavaType cArg = cArgs[i]; if (bArg.getName().equals(cArg.getName())) { continue; } // TODO: Check assignability... String[] args = { "" + (i + 1), cArg.toString() }; fireDiff(MSG_METHOD_PARAMTYPE_CHANGED, getSeverity(compatBaseline, baselineMethod, Severity.ERROR), compatBaseline, baselineMethod, args); } } private void checkReturnType(JavaType compatBaseline, Method baselineMethod, Method currentMethod) { JavaType bReturnType = baselineMethod.getReturnType(); JavaType cReturnType = currentMethod.getReturnType(); // TODO: Check assignability. If the new return type is // assignable to the old type, then the code is source-code // compatible even when binary-incompatible. if (!bReturnType.toString().equals(cReturnType.toString())) { fireDiff(MSG_METHOD_RETURNTYPE_CHANGED, getSeverity(compatBaseline, baselineMethod, Severity.ERROR), compatBaseline, baselineMethod, new String[] {cReturnType.toString()}); } } private void checkDeclaredExceptions( JavaType compatBaseline, Method baselineMethod, Method currentMethod) { // TODO } private void checkDeprecated( JavaType compatBaseline, Method baselineMethod, Method currentMethod) { boolean bIsDeprecated = baselineMethod.isDeprecated(); boolean cIsDeprecated = currentMethod.isDeprecated(); if (bIsDeprecated && !cIsDeprecated) { fireDiff(MSG_METHOD_UNDEPRECATED, Severity.INFO, compatBaseline, baselineMethod, null); } else if (!bIsDeprecated && cIsDeprecated) { fireDiff(MSG_METHOD_DEPRECATED, Severity.INFO, compatBaseline, baselineMethod, null); } } /** * Report changes in the declared accessibility of a method * (public/protected/etc). */ private void checkVisibility(JavaType compatBaseline, Method baselineMethod, Method currentMethod) { Scope bScope = baselineMethod.getEffectiveScope(); Scope cScope = currentMethod.getEffectiveScope(); if (cScope.isLessVisibleThan(bScope)) { String[] args = {bScope.getDesc(), cScope.getDesc()}; fireDiff(MSG_METHOD_LESS_ACCESSIBLE, getSeverity(compatBaseline, baselineMethod, Severity.ERROR), compatBaseline, baselineMethod, args); } else if (cScope.isMoreVisibleThan(bScope)) { String[] args = {bScope.getDesc(), cScope.getDesc()}; fireDiff(MSG_METHOD_MORE_ACCESSIBLE, Severity.INFO, compatBaseline, baselineMethod, args); } } private void checkFinal( JavaType compatBaseline, Method baselineMethod, Method currentMethod) { boolean bIsFinal = baselineMethod.isFinal(); boolean cIsFinal = currentMethod.isFinal(); if (bIsFinal && !cIsFinal) { fireDiff(MSG_METHOD_NOW_NONFINAL, Severity.INFO, compatBaseline, baselineMethod, null); } else if (!bIsFinal && cIsFinal) { fireDiff(MSG_METHOD_NOW_FINAL, Severity.ERROR, compatBaseline, baselineMethod, null); } } /** * Creates a human readable String that is similar to the method signature * and identifies the method within a class. * @param clazz the container of the method * @param method the method to identify. * @return a human readable id, for example "public void print(java.lang.String)" */ private String getMethodId(JavaType clazz, Method method) { StringBuffer buf = new StringBuffer(); final String scopeDecl = method.getDeclaredScope().getDecl(); if (scopeDecl.length() > 0) { buf.append(scopeDecl); buf.append(" "); } String name = method.getName(); if ("".equals(name)) { final String className = clazz.getName(); int idx = className.lastIndexOf('.'); name = className.substring(idx + 1); } else { buf.append(method.getReturnType()); buf.append(' '); } buf.append(name); buf.append('('); appendHumanReadableArgTypeList(method, buf); buf.append(')'); return buf.toString(); } private void appendHumanReadableArgTypeList(Method method, StringBuffer buf) { JavaType[] argTypes = method.getArgumentTypes(); String argSeparator = ""; for (int i = 0; i < argTypes.length; i++) { buf.append(argSeparator); buf.append(argTypes[i].getName()); argSeparator = ", "; } } private void fireDiff(Message msg, Severity severity, JavaType clazz, Method method, String[] args) { final String className = clazz.getName(); final ApiDifference diff = new ApiDifference( msg, severity, className, getMethodId(clazz, method), null, args); getApiDiffDispatcher().fireDiff(diff); } } ./clirr-0.6/core/src/java/net/sf/clirr/core/internal/AbstractDiffReporter.java0000644000175000017500000001325210316146510027224 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core.internal; import net.sf.clirr.core.ApiDifference; import net.sf.clirr.core.Severity; import net.sf.clirr.core.Message; import net.sf.clirr.core.spi.JavaType; import net.sf.clirr.core.spi.Field; import net.sf.clirr.core.spi.Method; import net.sf.clirr.core.spi.Scope; public abstract class AbstractDiffReporter { private static final Message MSG_UNABLE_TO_DETERMINE_CLASS_SCOPE = new Message(9000); private ApiDiffDispatcher dispatcher; public AbstractDiffReporter(ApiDiffDispatcher dispatcher) { this.dispatcher = dispatcher; } protected final ApiDiffDispatcher getApiDiffDispatcher() { return dispatcher; } protected final void log( Message msg, Severity severity, String clazz, Method method, Field field, String[] args) { final ApiDifference diff = new ApiDifference( msg, severity, clazz, null, null, args); getApiDiffDispatcher().fireDiff(diff); } /** * Determine whether the severity of the problem should be reduced * to INFO because the specified class is package or private accessibility. * Clirr reports changes at level INFO for all private and package * scoped objects. *

* Note that the class passed here should always be from the old * class version, because we're checking whether existing code * would have been able to access it (potential compatibility problems) * or not. * * @param clazz is the class the change is being reported about. * @param sev is the severity that should be reported for public/protected * scoped classes. * * @return param sev if the class is public/protected, and Severity.INFO * if the class is package or private scope. */ protected final Severity getSeverity(JavaType clazz, Severity sev) { Scope scope = clazz.getEffectiveScope(); if (scope.isLessVisibleThan(Scope.PROTECTED)) { return Severity.INFO; } else { return sev; } } /** * Determine whether the severity of the problem should be reduced * to INFO because: *

    *
  • the specified method is package or private accessibility, or
  • *
  • the specified method is in a package or private class.
  • *

    * Clirr reports changes at level INFO for all private and package * scoped objects. *

    * Note that the method passed here should always be from the old * class version, because we're checking whether existing code * would have been able to access it (potential compatibility problems) * or not. * * @param clazz is the class containing the method of interest * @param method is the method the change is being reported about. * @param sev is the severity that should be reported for public/protected * scoped methods. * * @return param sev if the method is public/protected, and Severity.INFO * if the method is package or private scope. */ protected final Severity getSeverity(JavaType clazz, Method method, Severity sev) { if (!method.getDeclaredScope().isLessVisibleThan(Scope.PROTECTED)) { return getSeverity(clazz, sev); } else { return Severity.INFO; } } /** * Determine whether the severity of the problem should be reduced * to INFO because: *

      *
    • the specified field is package or private accessibility, or
    • *
    • the specified field is in a package or private class.
    • *
    *

    * Clirr reports changes at level INFO for all private and package * scoped objects. *

    * Note that the field passed here should always be from the old * class version, because we're checking whether existing code * would have been able to access it (potential compatibility problems) * or not. * * @param clazz is the class containing the method of interest * @param field is the field the change is being reported about. * @param sev is the severity that should be reported for public/protected * scoped field. * * @return param sev if the field is public/protected, and Severity.INFO * if the field is package or private scope. */ protected final Severity getSeverity(JavaType clazz, Field field, Severity sev) { if (!field.getDeclaredScope().isLessVisibleThan(Scope.PROTECTED)) { return getSeverity(clazz, sev); } else { return Severity.INFO; } } } ./clirr-0.6/core/src/java/net/sf/clirr/core/internal/CoIterator.java0000644000175000017500000001412710316146510025222 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core.internal; import java.util.Collection; import java.util.Comparator; import java.util.Arrays; import java.util.NoSuchElementException; /** * This is an iterator that walks a pair of collections, returning * matching pairs from the set. *

    * When an element is present in the left set but there is no equal object * in the right set, the pair (leftobj, null) is returned. *

    * When an element is present in the right set but there is no equal object * in the left set, the pair (null, rightobj) is returned. *

    * When an element in one set has an equal element in the other set, the * pair (leftobj, rightobj) is returned. *

    * Note that the phrase "pair is returned" above actually means that the * getLeft and getRight methods on the iterator return those objects; the * pair is "conceptual" rather than a physical Pair instance. This avoids * instantiating an object to represent the pair for each step of the * iterator which would not be efficient. *

    * Note also that elements from the sets are always returned in the order * defined by the provided comparator. * * @author Simon Kitching. */ public final class CoIterator { private Object[] left; private Object[] right; private int leftIndex; private int rightIndex; private Object currLeft; private Object currRight; private Comparator comparator; /** * Iterate over the two collections, using the provided comparator. *

    * The collections are not modified by this iterator. * * @param comparator is used to compare elements from the two collections. * If null, then the objects in the collections are expected to implement * the Comparable interface. */ public CoIterator(Comparator comparator, Collection left, Collection right) { this.comparator = comparator; this.left = left.toArray(); this.right = right.toArray(); Arrays.sort(this.left, comparator); Arrays.sort(this.right, comparator); } /** * Iterate over the objects in the two arrays, using the provided comparator. *

    * The arrays are not modified by this iterator. In particular, the * iterator returns the elements in ascending order, but the actual * arrays passed in here are cloned so that they are not modified. * * @param comparator is used to compare elements from the two collections. * If null, then the objects in the collections are expected to implement * the Comparable interface. */ public CoIterator(Comparator comparator, Object[] left, Object[] right) { this.comparator = comparator; this.left = (Object[]) left.clone(); this.right = (Object[]) right.clone(); Arrays.sort(this.left, comparator); Arrays.sort(this.right, comparator); } /** * Indicates whether there are any more elements to be returned. */ public boolean hasNext() { return (leftIndex < left.length) || (rightIndex < right.length); } /** * Moves this iterator object to refer to the next "pair" of objects. *

    * Note that unlike the standard java.util.Iterator, this method does * not return anything; it simply modifies which objects will be * returned by the getLeft and getRight methods. * * @throws java.util.NoSuchElementException if this is called when hasNext would * report false (this is standard iterator behaviour). */ public void next() { boolean haveLeft = leftIndex < left.length; boolean haveRight = rightIndex < right.length; if (!haveLeft && !haveRight) { currLeft = null; currRight = null; throw new NoSuchElementException(); } int order; if (haveLeft && !haveRight) { order = -1; } else if (!haveLeft && haveRight) { order = +1; } else if (comparator != null) { order = comparator.compare(left[leftIndex], right[rightIndex]); } else { Comparable c1 = (Comparable) left[leftIndex]; order = c1.compareTo(right[rightIndex]); } if (order < 0) { currLeft = left[leftIndex]; currRight = null; ++leftIndex; } else if (order > 0) { currLeft = null; currRight = right[rightIndex]; ++rightIndex; } else { currLeft = left[leftIndex]; currRight = right[rightIndex]; ++leftIndex; ++rightIndex; } } /** * Return an object from the "left" collection specified to this object's * constructor. When the iterator has selected an element in the "right" * collection for which there is no corresponding element in the left * collection, then this will return null. */ public Object getLeft() { return currLeft; } /** * See getLeft. */ public Object getRight() { return currRight; } } ./clirr-0.6/core/src/java/net/sf/clirr/core/internal/package.html0000644000175000017500000000015010316146510024554 0ustar twernertwerner The framework that is used by the individual checks in the checks package. ./clirr-0.6/core/src/java/net/sf/clirr/core/internal/ExceptionUtil.java0000644000175000017500000000511710316146510025742 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core.internal; import java.lang.reflect.Method; /** * A helper class to initialize the cause of an exception. * * This allows Clirr to be compile time compatible with JDK 1.3 * at still taking advantage of the JDK 1.4 chained exceptions feature * if available. * * @author lkuehne */ public final class ExceptionUtil { /** Disallow instantiation. */ private ExceptionUtil() { } private static Method initCauseMethod; static { try { initCauseMethod = Throwable.class.getMethod("initCause", new Class[]{Throwable.class}); } catch (NoSuchMethodException e) { // we're on JDK < 1.4, no cause data will be available in Exception stacktraces initCauseMethod = null; } } /** * Initializes the chained exception if possible. * Does nothing if chained exceptions are not available on the * current JDK (1.3 or lower). * * @param t the resulting exception (high abstraction level) * @param cause the underlying cause of t (low abstraction level) */ public static void initCause(Throwable t, Throwable cause) { if (initCauseMethod == null) { return; } try { initCauseMethod.invoke(t, new Throwable[]{cause}); } catch (Exception e) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } throw new RuntimeException("unable to initCause: " + e.toString()); } } } ./clirr-0.6/core/src/java/net/sf/clirr/core/internal/ApiDiffDispatcher.java0000644000175000017500000000252310316146510026455 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core.internal; import net.sf.clirr.core.ApiDifference; /** * Distributes API differences to listeners. */ public interface ApiDiffDispatcher { /** * Called by checks to inform listeners that an API difference has been detected. * @param diff the API difference */ void fireDiff(ApiDifference diff); } ./clirr-0.6/core/src/java/net/sf/clirr/core/internal/ClassChangeCheck.java0000644000175000017500000000236110316146510026255 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core.internal; import net.sf.clirr.core.CheckerException; import net.sf.clirr.core.spi.JavaType; public interface ClassChangeCheck { boolean check(JavaType compatBaseline, JavaType currentVersion) throws CheckerException; } ./clirr-0.6/core/src/java/net/sf/clirr/core/internal/ClassLoaderUtil.java0000644000175000017500000000240610316146510026176 0ustar twernertwernerpackage net.sf.clirr.core.internal; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; /** * Helper class for dealing with ClassLoaders. * @author lk */ public final class ClassLoaderUtil { /** prevent instatiation. */ private ClassLoaderUtil() { } /** * @param cpEntries * @return */ public static ClassLoader createClassLoader(final String[] cpEntries) { final URL[] cpUrls = new URL[cpEntries.length]; for (int i = 0; i < cpEntries.length; i++) { String cpEntry = cpEntries[i]; File entry = new File(cpEntry); try { URL url = entry.toURL(); cpUrls[i] = url; } catch (MalformedURLException ex) { final IllegalArgumentException illegalArgEx = new IllegalArgumentException( "Cannot create classLoader from classpath entry " + entry); ExceptionUtil.initCause(illegalArgEx, ex); throw illegalArgEx; } } final URLClassLoader classPathLoader = new URLClassLoader(cpUrls); return classPathLoader; } } ./clirr-0.6/core/src/java/net/sf/clirr/core/internal/bcel/0000755000175000017500000000000010316146510023204 5ustar twernertwerner./clirr-0.6/core/src/java/net/sf/clirr/core/internal/bcel/package.html0000644000175000017500000000023310316146510025463 0ustar twernertwerner Apache's BCEL is currently used to read the compiled Java classes. ./clirr-0.6/core/src/java/net/sf/clirr/core/internal/bcel/BcelJavaType.java0000644000175000017500000000754710316146510026375 0ustar twernertwernerpackage net.sf.clirr.core.internal.bcel; import net.sf.clirr.core.spi.Field; import net.sf.clirr.core.spi.JavaType; import net.sf.clirr.core.spi.Method; import net.sf.clirr.core.spi.Scope; import org.apache.bcel.classfile.JavaClass; import org.apache.bcel.generic.ObjectType; import org.apache.bcel.generic.Type; import org.apache.bcel.util.Repository; /** * */ public final class BcelJavaType implements JavaType { private Type type; private JavaClass clazz; private Repository repository; public BcelJavaType(Type bcelType, Repository repository) { this.type = bcelType; this.repository = repository; this.clazz = findJavaClass(type); } public BcelJavaType(JavaClass clazz) { this.type = null; // TODO: how can I convert a JavaClass to the corresponding Type? this.repository = clazz.getRepository(); this.clazz = clazz; } public String getName() { if (clazz != null) { return clazz.getClassName(); } else { return type.toString(); } } public JavaType getContainingClass() { // TODO: move code from ScopeHelper here return null; } private JavaClass findJavaClass(Type type) { if (!(type instanceof ObjectType)) { return null; } ObjectType ot = (ObjectType) type; return repository.findClass(ot.getClassName()); } public JavaType[] getSuperClasses() { if (clazz == null) { return new JavaType[0]; } final JavaClass[] superClasses = clazz.getSuperClasses(); return convertToJavaTypeArray(superClasses); } public JavaType[] getAllInterfaces() { if (clazz == null) { return new JavaType[0]; } final JavaClass[] interfaces = clazz.getAllInterfaces(); return convertToJavaTypeArray(interfaces); } public JavaType[] getInnerClasses() { return new JavaType[0]; } public Method[] getMethods() { if (clazz == null) { return new Method[0]; } final org.apache.bcel.classfile.Method[] methods = clazz.getMethods(); Method[] ret = new Method[methods.length]; for (int i = 0; i < ret.length; i++) { ret[i] = new BcelMethod(clazz, methods[i]); } return ret; } public Field[] getFields() { if (clazz == null) { return new Field[0]; } final org.apache.bcel.classfile.Field[] fields = clazz.getFields(); Field[] ret = new Field[fields.length]; for (int i = 0; i < ret.length; i++) { ret[i] = new BcelField(clazz, fields[i]); } return ret; } public boolean isPrimitive() { return clazz == null; } public boolean isArray() { return false; } public boolean isFinal() { if (clazz == null) { return false; } return clazz.isFinal(); } public boolean isAbstract() { if (clazz == null) { return false; } return clazz.isAbstract(); } public boolean isInterface() { return clazz.isInterface(); } public Scope getDeclaredScope() { return BcelScopeHelper.getClassScope(clazz); } public Scope getEffectiveScope() { return getDeclaredScope(); // FIXME } public String toString() { return getName(); } /** * @param bcelClasses * @return */ private JavaType[] convertToJavaTypeArray(final JavaClass[] bcelClasses) { JavaType[] ret = new JavaType[bcelClasses.length]; for (int i = 0; i < ret.length; i++) { ret[i] = new BcelJavaType(bcelClasses[i]); } return ret; } } ./clirr-0.6/core/src/java/net/sf/clirr/core/internal/bcel/BcelMethod.java0000644000175000017500000000403110316146510026053 0ustar twernertwernerpackage net.sf.clirr.core.internal.bcel; import org.apache.bcel.classfile.Attribute; import org.apache.bcel.classfile.JavaClass; import org.apache.bcel.classfile.Method; import org.apache.bcel.generic.Type; import net.sf.clirr.core.spi.JavaType; import net.sf.clirr.core.spi.Scope; final class BcelMethod implements net.sf.clirr.core.spi.Method { private Method method; private JavaClass owningClass; public BcelMethod(JavaClass owningClass, Method method) { this.owningClass = owningClass; this.method = method; } public JavaType getReturnType() { return convertType(method.getReturnType()); } public String getName() { return method.getName(); } public boolean isFinal() { return method.isFinal(); } public boolean isStatic() { return method.isStatic(); } public boolean isAbstract() { return method.isAbstract(); } public boolean isDeprecated() { Attribute[] attrs = method.getAttributes(); for (int i = 0; i < attrs.length; ++i) { if (attrs[i] instanceof org.apache.bcel.classfile.Deprecated) { return true; } } return false; } public Scope getDeclaredScope() { return BcelScopeHelper.getScope(method.getAccessFlags()); } public Scope getEffectiveScope() { // TODO: real impl return getDeclaredScope(); } public JavaType[] getArgumentTypes() { final Type[] types = method.getArgumentTypes(); return convertTypes(types); } private JavaType convertType(Type bcelType) { return new BcelJavaType(bcelType, owningClass.getRepository()); } /** * @param types * @return */ private JavaType[] convertTypes(final Type[] types) { JavaType[] retval = new JavaType[types.length]; for (int i = 0; i < retval.length; i++) { retval[i] = convertType(types[i]); } return retval; } } ./clirr-0.6/core/src/java/net/sf/clirr/core/internal/bcel/BcelScopeHelper.java0000644000175000017500000001153410316146510027052 0ustar twernertwernerpackage net.sf.clirr.core.internal.bcel; import net.sf.clirr.core.CheckerException; import net.sf.clirr.core.spi.Scope; import org.apache.bcel.Constants; import org.apache.bcel.classfile.Attribute; import org.apache.bcel.classfile.ConstantClass; import org.apache.bcel.classfile.ConstantPool; import org.apache.bcel.classfile.ConstantUtf8; import org.apache.bcel.classfile.InnerClass; import org.apache.bcel.classfile.InnerClasses; import org.apache.bcel.classfile.JavaClass; import org.apache.bcel.util.Repository; final class BcelScopeHelper { /** * * */ private BcelScopeHelper() { } /** * Get a Scope object representing the accessibility of the specified * object. *

    * Note that this method gives the wrong results for JavaClass objects * which are nested classes. Use getClassScope(jclass) instead. */ public static Scope getScope(int accessFlags) { if ((accessFlags & Constants.ACC_PUBLIC) > 0) { return Scope.PUBLIC; } if ((accessFlags & Constants.ACC_PROTECTED) > 0) { return Scope.PROTECTED; } if ((accessFlags & Constants.ACC_PRIVATE) > 0) { return Scope.PRIVATE; } return Scope.PACKAGE; } /** * Java class files only ever contain scope specifiers of "public" or * "package". For top-level classes, this is expected: it is not possible * to have a top-level protected or private class. *

    * However nested classes can be declared as protected or private. The * way to tell the real scope of a nested class is to ignore the scope in * the actual class file itself, and instead look in the "InnerClasses" * attribute stored on the enclosing class. This is exactly what the java * compiler does when compiling, and what the jvm does when verifying class * linkage at runtime. *

    * For a "top-level" class, this method just returns the access scope for * the class itself. For nested classes, the enclosing class of the * specified class is retrieved and its InnerClasses attribute checked to * find the true scope for the specified class. *

    * @throws CheckerException if the specified class is a nested class and * the enclosing class could not be found, or if the supposedly enclosing * class has no reference to the nested class. This exception is not * expected to occur in practice, unless a truly screwed-up jar file is * passed to clirr for inspection. */ public static Scope getClassScope(JavaClass jclass) throws CheckerException { int dollarPos = jclass.getClassName().lastIndexOf('$'); if (dollarPos == -1) { // not a nested class return getScope(jclass.getAccessFlags()); } // ok this is a nested class String jclassName = jclass.getClassName(); String enclosingClassName = jclassName.substring(0, dollarPos); Repository repo = jclass.getRepository(); JavaClass enclosingClass = repo.findClass(enclosingClassName); if (enclosingClass == null) { throw new CheckerException( "Unable to locate enclosing class " + enclosingClassName + " for nested class " + jclassName); } ConstantPool pool = enclosingClass.getConstantPool(); Attribute[] attrs = enclosingClass.getAttributes(); for (int i = 0; i < attrs.length; ++i) { if (attrs[i] instanceof InnerClasses) { InnerClasses ics = (InnerClasses) attrs[i]; InnerClass[] icarray = ics.getInnerClasses(); for (int j = 0; j < icarray.length; ++j) { // in the code below, instanceof checks should not be necessary // before casting Constants because the classfile format ensures // that instanceof would always be true InnerClass ic = icarray[j]; int classIndex = ic.getInnerClassIndex(); ConstantClass constClass = (ConstantClass) pool.getConstant(classIndex); int nameIndex = constClass.getNameIndex(); ConstantUtf8 nameconst = (ConstantUtf8) pool.getConstant(nameIndex); String classname = nameconst.getBytes().replace('/', '.'); if (jclassName.equals(classname)) { return getScope(ic.getInnerAccessFlags()); } } } } // weird; no nested class info found throw new CheckerException( "Unable to find information in class " + enclosingClass.getClassName() + " referring back to nested class " + jclassName); } } ./clirr-0.6/core/src/java/net/sf/clirr/core/internal/bcel/BcelField.java0000644000175000017500000000301210316146510025654 0ustar twernertwernerpackage net.sf.clirr.core.internal.bcel; import org.apache.bcel.classfile.Attribute; import org.apache.bcel.classfile.Field; import org.apache.bcel.classfile.JavaClass; import net.sf.clirr.core.spi.JavaType; import net.sf.clirr.core.spi.Scope; final class BcelField implements net.sf.clirr.core.spi.Field { private final Field field; private final JavaClass owningClass; BcelField(JavaClass owningClass, Field field) { this.owningClass = owningClass; this.field = field; } public String getName() { return field.getName(); } public JavaType getType() { return new BcelJavaType(field.getType(), owningClass.getRepository()); } public boolean isFinal() { return field.isFinal(); } public boolean isStatic() { return field.isStatic(); } public boolean isDeprecated() { Attribute[] attrs = field.getAttributes(); for (int i = 0; i < attrs.length; ++i) { if (attrs[i] instanceof org.apache.bcel.classfile.Deprecated) { return true; } } return false; } public Object getConstantValue() { return field.getConstantValue(); } public Scope getDeclaredScope() { return BcelScopeHelper.getScope(field.getAccessFlags()); } public Scope getEffectiveScope() { return getDeclaredScope(); // FIXME } public String toString() { return field.toString(); } } ./clirr-0.6/core/src/java/net/sf/clirr/core/internal/bcel/BcelTypeArrayBuilder.java0000644000175000017500000001225510316146510030071 0ustar twernertwernerpackage net.sf.clirr.core.internal.bcel; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import net.sf.clirr.core.CheckerException; import net.sf.clirr.core.ClassFilter; import net.sf.clirr.core.ClassSelector; import net.sf.clirr.core.internal.ExceptionUtil; import net.sf.clirr.core.spi.JavaType; import org.apache.bcel.classfile.ClassParser; import org.apache.bcel.classfile.JavaClass; import org.apache.bcel.util.ClassLoaderRepository; import org.apache.bcel.util.Repository; public final class BcelTypeArrayBuilder { /** * Disallow instantiation. */ private BcelTypeArrayBuilder() { } /** * Creates a set of classes to check. * * @param jarFiles a set of jar filed to scan for class files. * * @param thirdPartyClasses loads classes that are referenced * by the classes in the jarFiles * * @param classSelector is an object which determines which classes from the * old and new jars are to be compared. This parameter may be null, in * which case all classes in the old and new jars are compared. */ public static JavaType[] createClassSet( File[] jarFiles, ClassLoader thirdPartyClasses, ClassFilter classSelector) throws CheckerException { if (classSelector == null) { // create a class selector that selects all classes classSelector = new ClassSelector(ClassSelector.MODE_UNLESS); } ClassLoader classLoader = createClassLoader(jarFiles, thirdPartyClasses); Repository repository = new ClassLoaderRepository(classLoader); List selected = new ArrayList(); for (int i = 0; i < jarFiles.length; i++) { File jarFile = jarFiles[i]; ZipFile zip = null; try { zip = new ZipFile(jarFile, ZipFile.OPEN_READ); } catch (IOException ex) { throw new CheckerException( "Cannot open " + jarFile + " for reading", ex); } Enumeration enumEntries = zip.entries(); while (enumEntries.hasMoreElements()) { ZipEntry zipEntry = (ZipEntry) enumEntries.nextElement(); if (!zipEntry.isDirectory() && zipEntry.getName().endsWith(".class")) { JavaClass clazz = extractClass(zipEntry, zip, repository); if (classSelector.isSelected(clazz)) { selected.add(new BcelJavaType(clazz)); repository.storeClass(clazz); } } } } JavaType[] ret = new JavaType[selected.size()]; selected.toArray(ret); return ret; } private static ClassLoader createClassLoader( File[] jarFiles, ClassLoader thirdPartyClasses) { final URL[] jarUrls = new URL[jarFiles.length]; for (int i = 0; i < jarFiles.length; i++) { File jarFile = jarFiles[i]; try { URL url = jarFile.toURL(); jarUrls[i] = url; } catch (MalformedURLException ex) { // this should never happen final IllegalArgumentException illegalArgumentException = new IllegalArgumentException( "Cannot create classloader with jar file " + jarFile); ExceptionUtil.initCause(illegalArgumentException, ex); throw illegalArgumentException; } } final URLClassLoader jarsLoader = new URLClassLoader(jarUrls, thirdPartyClasses); return jarsLoader; } private static JavaClass extractClass( ZipEntry zipEntry, ZipFile zip, Repository repository) throws CheckerException { String name = zipEntry.getName(); InputStream is = null; try { is = zip.getInputStream(zipEntry); ClassParser parser = new ClassParser(is, name); JavaClass clazz = parser.parse(); clazz.setRepository(repository); return clazz; } catch (IOException ex) { throw new CheckerException( "Cannot read " + zipEntry.getName() + " from " + zip.getName(), ex); } finally { if (is != null) { try { is.close(); } catch (IOException ex) { throw new CheckerException("Cannot close " + zip.getName(), ex); } } } } } ./clirr-0.6/core/src/java/net/sf/clirr/core/internal/NameComparator.java0000644000175000017500000000106710316146510026056 0ustar twernertwernerpackage net.sf.clirr.core.internal; import java.util.Comparator; import net.sf.clirr.core.spi.Named; /** * Compares {@link Named named entities} by their name. * * @author Simon Kitching * @author lkuehne */ public final class NameComparator implements Comparator { public NameComparator() { } public int compare(Object o1, Object o2) { Named f1 = (Named) o1; Named f2 = (Named) o2; final String name1 = f1.getName(); final String name2 = f2.getName(); return name1.compareTo(name2); } } ./clirr-0.6/core/src/java/net/sf/clirr/core/spi/0000755000175000017500000000000010316146510021256 5ustar twernertwerner./clirr-0.6/core/src/java/net/sf/clirr/core/spi/JavaType.java0000644000175000017500000000340710316146510023650 0ustar twernertwernerpackage net.sf.clirr.core.spi; /** * A Java Type (Object, Interface, primitive type or void). * * @author lkuehne */ public interface JavaType extends Named, Scoped { /** * Type fully qualified class name. * * @return a fully qualified class name, * like "my.company.procuct.SampleClass". */ String getName(); /** * The containing class if this is an inner class. * * @return the containing class or null * if this JavaType does not represent an inner class. */ JavaType getContainingClass(); /** * Return the superclasses of this class. * * @return the chain of superclasses of this type, starting from * the direct superclass and ending with java.lang.Object. */ JavaType[] getSuperClasses(); /** * Return the list of all interfaces this class implements. * * @return the list of all interfaces this class implements/extends, * excluding this if this JavaType represents an interface itself. */ JavaType[] getAllInterfaces(); JavaType[] getInnerClasses(); /** * All methods that are declared by this class. * Methods of superclasses/interfaces are not returned * if they are not overridden/redeclared here. * * @return all methods that are declared by this class. */ Method[] getMethods(); /** * All fields that are declared by this class. * Fields of superclasses/interfaces are not returned. * * @return all fields that are declared by this class. */ Field[] getFields(); boolean isPrimitive(); boolean isArray(); boolean isFinal(); boolean isAbstract(); boolean isInterface(); } ./clirr-0.6/core/src/java/net/sf/clirr/core/spi/Scope.java0000644000175000017500000000277310316146510023203 0ustar twernertwernerpackage net.sf.clirr.core.spi; /** * Enumeration type that represents an "accessibility" level for * a java class, field or method. *

    * Change of access rights from lower to higher visibility rating is a * binary-compatible change. Change of access rights from higher to * lower is a binary-incompatible change. *

    * Public > Protected > Package > Private * * @author Simon Kitching */ public final class Scope { private int vis; private String desc; private String decl; /** Object representing private scoped objects. */ public static final Scope PRIVATE = new Scope(0, "private", "private"); /** Object representing package scoped objects. */ public static final Scope PACKAGE = new Scope(1, "package", ""); /** Object representing protected scoped objects. */ public static final Scope PROTECTED = new Scope(2, "protected", "protected"); /** Object representing public scoped objects. */ public static final Scope PUBLIC = new Scope(3, "public", "public"); private Scope(int vis, String desc, String decl) { this.vis = vis; this.desc = desc; this.decl = decl; } public boolean isMoreVisibleThan(Scope v) { return this.vis > v.vis; } public boolean isLessVisibleThan(Scope v) { return this.vis < v.vis; } public String getDesc() { return desc; } /** the Java visibility modifier. **/ public String getDecl() { return decl; } } ./clirr-0.6/core/src/java/net/sf/clirr/core/spi/package.html0000644000175000017500000000111710316146510023537 0ustar twernertwerner Service Provider Interface for letting Clirr know about Java types, methods, etc. This package is intended to

    • Decouple the current BCEL implementation from the comparison algorithms (separation of concerns).
    • Allow IDE plugins to provide an implementation that is based on the internal Java representation of the IDE. This allows instantatious error marker feedback, building class files or jars on disk is not required.
    ./clirr-0.6/core/src/java/net/sf/clirr/core/spi/Named.java0000644000175000017500000000033710316146510023150 0ustar twernertwernerpackage net.sf.clirr.core.spi; /** * A named entity in the Java programming language. * * @author lkuehne */ public interface Named { /** * Returns the name of this object. */ String getName(); } ./clirr-0.6/core/src/java/net/sf/clirr/core/spi/Method.java0000644000175000017500000000104410316146510023340 0ustar twernertwernerpackage net.sf.clirr.core.spi; /** * Describes a Java method. */ public interface Method extends Named, Scoped { /** * * @return the return type of this method, or null if the method return type is void */ JavaType getReturnType(); /** * * @return the argument types of this method, never null. */ JavaType[] getArgumentTypes(); // JavaType[] getDeclaredExceptions(); boolean isFinal(); boolean isStatic(); boolean isAbstract(); boolean isDeprecated(); } ./clirr-0.6/core/src/java/net/sf/clirr/core/spi/Scoped.java0000644000175000017500000000137210316146510023341 0ustar twernertwernerpackage net.sf.clirr.core.spi; /** * A Java source code entity like a type or a method that has the * concept of a visibility scope. * * Each entity has two scopes: One that is declared and the effective scope. * For example a public method can have an effective scope of package if it * appears in a class that is package visible. * * @author lk * */ public interface Scoped { /** * The declared scope of this entity. * @return the scope that appears in the modifiers of this entity. */ Scope getDeclaredScope(); /** * The effective Scope of this entity. * * @return the minimum scope of the modifiers of this entity and * it's all of it's containers. */ Scope getEffectiveScope(); } ./clirr-0.6/core/src/java/net/sf/clirr/core/spi/Field.java0000644000175000017500000000152310316146510023145 0ustar twernertwernerpackage net.sf.clirr.core.spi; /** * Describes a field of a class. */ public interface Field extends Named, Scoped { /** * The type of this field. */ JavaType getType(); /** * Whether the field is declared as final. */ boolean isFinal(); /** * Whether the field is declared as static. */ boolean isStatic(); /** * Whether the field is deprecated. */ boolean isDeprecated(); /** * Returns the constant value of this field. * The constant value is an Object if the field is static and final and the java compiler * could calculate the value at compilation time. * * @return the constant value or null if the compiler could * not calculate the value at compilation time */ Object getConstantValue(); } ./clirr-0.6/core/src/java/net/sf/clirr/core/Checker.java0000644000175000017500000001525010316146510022675 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import net.sf.clirr.core.internal.ApiDiffDispatcher; import net.sf.clirr.core.internal.ClassChangeCheck; import net.sf.clirr.core.internal.CoIterator; import net.sf.clirr.core.internal.NameComparator; import net.sf.clirr.core.internal.checks.ClassHierarchyCheck; import net.sf.clirr.core.internal.checks.ClassModifierCheck; import net.sf.clirr.core.internal.checks.ClassScopeCheck; import net.sf.clirr.core.internal.checks.FieldSetCheck; import net.sf.clirr.core.internal.checks.GenderChangeCheck; import net.sf.clirr.core.internal.checks.InterfaceSetCheck; import net.sf.clirr.core.internal.checks.MethodSetCheck; import net.sf.clirr.core.spi.JavaType; import net.sf.clirr.core.spi.Scope; /** * This is the main class to be used by Clirr frontends, * it implements the checking functionality of Clirr. * Frontends can create an instance of this class * and register themselves as DiffListeners, they are then * informed whenever an API change is detected by the * reportDiffs method. * * @author lkuehne */ public final class Checker implements ApiDiffDispatcher { private static final Message MSG_CLASS_ADDED = new Message(8000); private static final Message MSG_CLASS_REMOVED = new Message(8001); private List listeners = new ArrayList(); private List classChecks = new ArrayList(); private ScopeSelector scopeSelector = new ScopeSelector(); /** * Package visible constructor for unit testing. */ Checker(ClassChangeCheck ccc) { if (ccc != null) { classChecks.add(ccc); } } /** * Creates a new Checker. */ public Checker() { classChecks.add(new ClassScopeCheck(this, scopeSelector)); classChecks.add(new GenderChangeCheck(this)); classChecks.add(new ClassModifierCheck(this)); classChecks.add(new InterfaceSetCheck(this)); classChecks.add(new ClassHierarchyCheck(this)); classChecks.add(new FieldSetCheck(this, scopeSelector)); classChecks.add(new MethodSetCheck(this, scopeSelector)); } public ScopeSelector getScopeSelector() { return scopeSelector; } public void addDiffListener(DiffListener listener) { listeners.add(listener); } private void fireStart() { for (Iterator it = listeners.iterator(); it.hasNext();) { DiffListener diffListener = (DiffListener) it.next(); diffListener.start(); } } private void fireStop() { for (Iterator it = listeners.iterator(); it.hasNext();) { DiffListener diffListener = (DiffListener) it.next(); diffListener.stop(); } } public void fireDiff(ApiDifference diff) { for (Iterator it = listeners.iterator(); it.hasNext();) { DiffListener diffListener = (DiffListener) it.next(); diffListener.reportDiff(diff); } } /** * Checks two sets of classes for api changes and reports * them to the DiffListeners. * @param compatibilityBaseline the classes that form the * compatibility baseline to check against * @param currentVersion the classes that are checked for * compatibility with compatibilityBaseline */ public void reportDiffs( JavaType[] compatibilityBaseline, JavaType[] currentVersion) throws CheckerException { fireStart(); runClassChecks(compatibilityBaseline, currentVersion); fireStop(); } private void runClassChecks( JavaType[] compat, JavaType[] current) throws CheckerException { CoIterator iter = new CoIterator( new NameComparator(), compat, current); while (iter.hasNext()) { iter.next(); JavaType compatBaselineClass = (JavaType) iter.getLeft(); JavaType currentClass = (JavaType) iter.getRight(); if (compatBaselineClass == null) { if (!scopeSelector.isSelected(currentClass.getEffectiveScope())) { continue; } final String className = currentClass.getName(); final ApiDifference diff = new ApiDifference( MSG_CLASS_ADDED, Severity.INFO, className, null, null, null); fireDiff(diff); } else if (currentClass == null) { final Scope classScope = compatBaselineClass.getEffectiveScope(); if (!scopeSelector.isSelected(classScope)) { continue; } final String className = compatBaselineClass.getName(); final Severity severity = classScope.isLessVisibleThan( Scope.PROTECTED) ? Severity.INFO : Severity.ERROR; final ApiDifference diff = new ApiDifference( MSG_CLASS_REMOVED, severity, className, null, null, null); fireDiff(diff); } else { // class is available in both releases boolean continueTesting = true; for (Iterator it = classChecks.iterator(); it.hasNext() && continueTesting;) { ClassChangeCheck classChangeCheck = (ClassChangeCheck) it.next(); continueTesting = classChangeCheck.check( compatBaselineClass, currentClass); } } } } } ./clirr-0.6/core/src/java/net/sf/clirr/core/DiffListener.java0000644000175000017500000000317610316146510023713 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core; /** * Listener for API differences. * * @author lkuehne */ public interface DiffListener { /** * Called when the listener should start listening. * This gives implementations a chance to write some header info. */ void start(); /** * Called when an API difference has been detected. * @param difference the difference that has been detected. */ void reportDiff(ApiDifference difference); /** * Called when the listener should stop listening. * This gives implementations a chance to write footer info, * close files, etc. */ void stop(); } ./clirr-0.6/core/src/java/net/sf/clirr/core/XmlDiffListener.java0000644000175000017500000000575010316146510024374 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core; import java.io.IOException; import java.io.PrintStream; /** * A DiffListener that reports any detected difference to * an XML file. That file can be used by subsequent processing steps * to create nice looking reports in HTML, PDF, etc. * * @author lkuehne */ public final class XmlDiffListener extends FileDiffListener { private static final String DIFFREPORT = "diffreport"; private static final String DIFFERENCE = "difference"; private MessageTranslator translator = new MessageTranslator(); public XmlDiffListener(String outFile) throws IOException { super(outFile); } public void reportDiff(ApiDifference difference) { PrintStream out = getOutputStream(); out.print(" <" + DIFFERENCE); out.print(" binseverity=\"" + difference.getBinaryCompatibilitySeverity() + "\""); out.print(" srcseverity=\"" + difference.getSourceCompatibilitySeverity() + "\""); out.print(" class=\"" + difference.getAffectedClass() + "\""); if (difference.getAffectedMethod() != null) { out.print(" method=\"" + difference.getAffectedMethod() + "\""); } if (difference.getAffectedField() != null) { out.print(" field=\"" + difference.getAffectedField() + "\""); } out.print(">"); out.print(difference.getReport(translator)); // TODO: XML escapes?? out.println("'); } /** * Writes an XML header and toplevel tag to the xml stream. * * @see DiffListener#start() */ public void start() { PrintStream out = getOutputStream(); out.println(""); out.println('<' + DIFFREPORT + '>'); } /** * Closes the toplevel tag that was opened in start. * * @see DiffListener#stop() */ protected void writeFooter() { PrintStream out = getOutputStream(); out.println("'); } } ./clirr-0.6/core/src/java/net/sf/clirr/core/ClassSelector.java0000644000175000017500000001355710316146510024107 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core; import org.apache.bcel.classfile.JavaClass; import java.util.ArrayList; import java.util.Iterator; /** * Given a JavaClass object, determines whether or not it is "selected", * based on its class or package. This is used to select subsets of the * classes available in a classpath for comparison or testing purposes. * * @author Simon Kitching */ public final class ClassSelector implements ClassFilter { /** Class for implementing an enumeration. */ public static final class Mode { private Mode() { } } /** positive selection. */ public static final Mode MODE_IF = new Mode(); /** negative selection. */ public static final Mode MODE_UNLESS = new Mode(); private Mode mode; private ArrayList packages = new ArrayList(); private ArrayList packageTrees = new ArrayList(); private ArrayList classes = new ArrayList(); /** * Create a selector. *

    * When mode is MODE_IF then a class is "selected" if-and-only-if * the class matches one of the criteria defined via the addXXX methods. * In other words, the criteria specify which classes are included * (selected) in the resulting class set. *

    * When mode is MODE_UNLESS, then a class is "selected" unless the class * matches one of the criteria defined via the addXXX methods. In other * words, the criteria specify which classes are excluded from the * resulting class set. */ public ClassSelector(Mode mode) { this.mode = mode; } /** * Matches any class which is in the named package. */ public void addPackage(String packageName) { packages.add(packageName); } /** * Matches any class which is in the named package or any subpackage of it. */ public void addPackageTree(String packageName) { packages.add(packageName); } /** * Matches the class with exactly this name, plus any of its inner classes. */ public void addClass(String classname) { classes.add(classname); } /** * Return true if this class is one selected by the criteria stored * in this object. */ public boolean isSelected(JavaClass clazz) { if (isAnonymousInnerClass(clazz)) { return false; } boolean matches = matchesCriteria(clazz); if (mode == MODE_IF) { return matches; } else // mode == MODE_UNLESS { return !matches; } } /** * Return true if this class is an anonymous inner class. * Not even developers working on a package would be interested * in API changes in these classes... */ private boolean isAnonymousInnerClass(JavaClass clazz) { String name = clazz.getClassName(); int dollarPos = name.indexOf('$'); if (dollarPos == -1) { return false; } for (int i = dollarPos + 1; i < name.length(); ++i) { if (!Character.isDigit(name.charAt(i))) { return false; } } // ok, we have a class name which contains a dollar sign, and // every subsequent character is a digit. return true; } /** * Return true if this class matches one of the criteria stored * in this object. */ private boolean matchesCriteria(JavaClass clazz) { String packageName = clazz.getPackageName(); if (packages.contains(packageName)) { return true; } for (Iterator i = packageTrees.iterator(); i.hasNext();) { String entry = (String) i.next(); if (packageName.startsWith(entry)) { if (packageName.length() == entry.length()) { // they are exactly equal return true; } if (packageName.charAt(entry.length()) == '.') { return true; } // else packagename is like "com.acmegadgets" and entryname // is like "com.acme", which is not a match, so keep looking. } } String className = clazz.getClassName(); for (Iterator i = classes.iterator(); i.hasNext();) { String entry = (String) i.next(); if (className.startsWith(entry)) { if (className.length() == entry.length()) { // they are exactly equal return true; } if (className.charAt(entry.length()) == '$') { // this is an inner class of the named class return true; } } } return false; } } ./clirr-0.6/core/src/java/net/sf/clirr/core/CheckerException.java0000644000175000017500000000341510316146510024554 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core; import net.sf.clirr.core.internal.ExceptionUtil; /** * An exception class representing a failure during checking of the * specified jar files. *

    * The Clirr coding conventions use checked exceptions (such as this one) * for errors whose cause is something external to the clirr library/app. * Unchecked exceptions are used for errors that are due to bugs within * clirr code (assertion-violation type problems). */ public class CheckerException extends RuntimeException // TODO: revert this back to Exception to match the class doc above { public CheckerException(String msg) { super(msg); } public CheckerException(String msg, Throwable cause) { super(msg); ExceptionUtil.initCause(this, cause); } } ./clirr-0.6/core/src/java/net/sf/clirr/core/MessageTranslator.java0000644000175000017500000001025710316146510024771 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core; import java.util.Locale; import java.util.Iterator; import java.util.Collection; import java.util.ResourceBundle; /** * Class which is capable of translating a Message object into a localised * string. */ public final class MessageTranslator { /** * The default base name of the resource bundle from which message * descriptions are read. */ public static final String DFLT_RESOURCE_NAME = EventMessages.class.getName(); private Locale locale = Locale.getDefault(); private String resourceName = DFLT_RESOURCE_NAME; private ResourceBundle messageText; /** * This is a singleton class; to get an instance of this class, use * the getInstance method. */ public MessageTranslator() { } /** * Define the local language etc. Future calls to the getDesc method * will attempt to use a properties file which is appropriate to that * locale to look the message descriptions up in. *

    * @param locale may be a valid Locale object, or null to indicate * that the default locale is to be used. */ public void setLocale(Locale locale) { if (locale == null) { locale = Locale.getDefault(); } this.locale = locale; this.messageText = null; } /** * Define the base name of the properties file that message * translations are to be read from. */ public void setResourceName(String resourceName) { this.resourceName = resourceName; this.messageText = null; } /** * Verify that the resource bundle for the currently set locale has * a translation string available for every message object in the provided * collection. This method is expected to be called from the unit tests, * so that if a developer adds a new message the unit tests will fail until * translations are also available for that new message. *

    * @throws java.util.MissingResourceException if there is a registered * message for which no description is present in the current locale's * resources. */ public void checkComplete(Collection messages) { for (Iterator i = messages.iterator(); i.hasNext();) { Message m = (Message) i.next(); getDesc(m); } } /** * Given a Message object (containing a unique message id), look up * that id in the appropriate resource bundle (properties file) for * the set locale and return the text string associated with that * message id. *

    * Message ids in the properties file should be prefixed with an 'm', * eg "m1000", "m5003". *

    * @throws java.util.MissingResourceException if there is no entry in the * message translation resource bundle for the specified message. */ public String getDesc(Message msg) { // load resource bundle if (locale == null) { locale = Locale.getDefault(); } if (messageText == null) { messageText = ResourceBundle.getBundle(resourceName, locale); } return messageText.getString("m" + msg.getId()); } } ./clirr-0.6/core/src/java/net/sf/clirr/core/package.html0000644000175000017500000000011410316146510022740 0ustar twernertwerner Clirr throws events when it finds api changes. ./clirr-0.6/core/src/java/net/sf/clirr/core/FileDiffListener.java0000644000175000017500000000522710316146510024512 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.OutputStream; import java.io.PrintStream; /** * Abstract DiffListener that writes output to some textual * output stream. That stream can either be System.out or a textfile. * * @author lkuehne */ abstract class FileDiffListener extends DiffListenerAdapter { private PrintStream outputStream; /** * Initializes the outputstream. * @param outFile the filename (System.out is used if null is provided here) * @throws java.io.FileNotFoundException if there are problems with */ FileDiffListener(String outFile) throws FileNotFoundException { if (outFile == null) { outputStream = System.out; } else { final OutputStream out = new FileOutputStream(outFile); outputStream = new PrintStream(out); } } /** * Returns the output stream so subclasses can write data. * @return the output stream */ protected final PrintStream getOutputStream() { return outputStream; } /** * Writes a footer and closes the * output stream if necessary. * * @see #writeFooter() */ public final void stop() { writeFooter(); if (outputStream != System.out) { outputStream.close(); } super.stop(); } /** * A hook to write footer info to the output stream. * This implementation does nothing, subclasses can override * this method if necessary. * * @see #stop() */ protected void writeFooter() { } } ./clirr-0.6/core/src/java/net/sf/clirr/core/MessageManager.java0000644000175000017500000000651610316146510024215 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core; import java.util.Iterator; import java.util.ArrayList; import java.util.Comparator; import java.util.Collection; /** * Class which manages API Difference messages, including expanding message * codes into strings and descriptions. */ public final class MessageManager { private static MessageManager instance; private ArrayList messages = new ArrayList(); /** * Utility class to sort messages by their numeric ids. */ private static class MessageComparator implements Comparator { public int compare(Object o1, Object o2) { Message m1 = (Message) o1; Message m2 = (Message) o2; return m1.getId() - m2.getId(); } } /** * This is a singleton class; to get an instance of this class, use * the getInstance method. */ private MessageManager() { } /** * Return the singleton instance of this class. */ public static MessageManager getInstance() { if (instance == null) { instance = new MessageManager(); } return instance; } /** * Add a message to the list of known messages. */ public void addMessage(Message msg) { messages.add(msg); } /** * Verify that the list of known messages contains no two objects * with the same numeric message id. This method is expected to be * called from the unit tests, so that if a developer adds a new * message and accidentally uses the message id of an existing * message object, then this will be reported as an error. *

    * @throws java.lang.IllegalArgumentException if any duplicate id is found. */ public void checkUnique() { java.util.Collections.sort(messages, new MessageComparator()); int lastId = -1; for (Iterator i = messages.iterator(); i.hasNext();) { // check for any duplicates Message m = (Message) i.next(); int currId = m.getId(); if (currId <= lastId) { throw new IllegalArgumentException("Message id [" + currId + "] is not unique."); } } } /** * Return the complete set of registered messages. */ public Collection getMessages() { return messages; } } ./clirr-0.6/core/src/java/net/sf/clirr/core/ScopeSelector.java0000644000175000017500000000663610316146510024113 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core; import net.sf.clirr.core.spi.Scope; import net.sf.clirr.core.spi.Scoped; /** * Selects zero or more java scope values (public, protected, package, * private). An instance of this class is used when comparing two versions * of an application to indicate what items are of interest. When the target * audience is "normal" users of the applications, only changes to items * which have public or protected scope are relevant. When the audience is * developers of the applications, then package-scope and private-scope * changes are also of interest. * * @author Simon Kitching */ public final class ScopeSelector { private Scope scope = Scope.PROTECTED; /** * Construct an instance which selects public and protected objects and * ignores package and private objects. The selectXXX methods can later * be used to adjust this default behaviour. */ public ScopeSelector() { } /** * Construct an instance which selects public and protected objects and * ignores package and private objects. The selectXXX methods can later * be used to adjust this default behaviour. */ public ScopeSelector(Scope scope) { this.scope = scope; } /** Specify which scope objects are of interest. */ public void setScope(Scope scope) { this.scope = scope; } /** * Get the scope that this object is configured with. */ public Scope getScope() { return scope; } /** * Return a string which indicates what scopes this object will consider * to be selected (ie relevant). */ public String toString() { return scope.getDesc(); } /** * Given a scoped object, return true if this object's scope is one of the * values this object is configured to match. * * @param scoped is the object whose scope is to be checked. * @return true if the object is selected. */ public boolean isSelected(Scoped scoped) { return !scoped.getEffectiveScope().isLessVisibleThan(scope); } /** * Return true if objects of the specified scope, or more visible, * are selected by this selector. * * @param scope is the scope being checked * @return true if objects of the specified scope are selected. */ public boolean isSelected(Scope scope) { return !scope.isLessVisibleThan(this.scope); } } ./clirr-0.6/core/src/java/net/sf/clirr/core/ApiDifference.java0000644000175000017500000002302310316146510024012 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core; /** * Describes an API change. * * @author Lars */ public final class ApiDifference { private static final int HASHCODE_MAGIC = 29; /** * Object representing the message text to be output (or null if * the constructor which takes a message string directly is used). */ private Message message = null; /** human readable change report. */ private String report; /** * severity of the change in terms of binary compatibility, * as determined by clirr. */ private Severity binaryCompatibilitySeverity; /** * severity of the change in terms of source compatibility, * as determined by clirr. */ private Severity sourceCompatibilitySeverity; /** The fully qualified class name that is affected by the API change. */ private String affectedClass; /** * The method that is affected, if any. *

    * The content is the method name plus the fully qualified * parameter types separated by comma and space and enclosed in * brackets, e.g. "doStuff(java.lang.String, int)". *

    * This value is null if no single method is * affected, i.e. if the * api change affects a field or is global * (like "class is now final"). */ private String affectedMethod; /** * The field that is affected, if any. *

    * The content is the field name, e.g. "someValue". * Type information for the field is not available. *

    * This value is null if no single field is * affected, i.e. if the * api change affects a method or is global * (like "class is now final"). */ private String affectedField; /** * The set of additional parameters that are available for use * when building the actual message description. These vary depending * upon the actual difference being reported. */ private String[] extraInfo; /** * Invokes the two-severity-level version of this constructor. */ public ApiDifference( Message message, Severity severity, String clazz, String method, String field, String[] args) { this(message, severity, severity, clazz, method, field, args); } /** * Create a new API difference representation. * * @param message is the key of a human readable string describing the * change that was made. * * @param binarySeverity the severity in terms of binary compatibility, * must be non-null. * * @param sourceSeverity the severity in terms of source code compatibility, * must be non-null. * * @param clazz is the fully-qualified name of the class in which the * change occurred, must be non-null. * * @param method the method signature of the method that changed, * null if no method was affected. * * @param field the field name where the change occured, null * if no field was affected. * * @param args is a set of additional change-specific strings which are * made available for the message description string to reference via * the standard {n} syntax. */ public ApiDifference( Message message, Severity binarySeverity, Severity sourceSeverity, String clazz, String method, String field, String[] args) { checkNonNull(message); checkNonNull(binarySeverity); checkNonNull(sourceSeverity); checkNonNull(clazz); this.message = message; this.binaryCompatibilitySeverity = binarySeverity; this.sourceCompatibilitySeverity = sourceSeverity; this.affectedClass = clazz; this.affectedField = field; this.affectedMethod = method; this.extraInfo = args; } /** * Trivial utility method to verify that a specific object is non-null. */ private void checkNonNull(Object o) { if (o == null) { throw new IllegalArgumentException(); } } /** * Return the message object (if any) associated with this difference. *

    * Checks which support the "new" message API will provide ApiDifference * objects with non-null message objects. */ public Message getMessage() { return message; } /** * The Severity of the API difference in terms of binary compatibility. * ERROR means that clients will definitely break, WARNING means that * clients may break, depending on how they use the library. * See the eclipse paper for further explanation. * * @return the severity of the API difference in terms of binary compatibility. */ public Severity getBinaryCompatibilitySeverity() { return binaryCompatibilitySeverity; } /** * The Severity of the API difference in terms of source compatibility. * Sometimes this is different than {@link #getBinaryCompatibilitySeverity * binary compatibility severity}, for example adding a checked exception * to a method signature is binary compatible but not source compatible. * ERROR means that clients will definitely break, WARNING means that * clients may break, depending on how they use the library. * See the eclipse paper for further explanation. * * @return the severity of the API difference in terms of source code * compatibility. */ public Severity getSourceCompatibilitySeverity() { return sourceCompatibilitySeverity; } /** * Return the maximum of the binary and source compatibility severities. */ public Severity getMaximumSeverity() { final Severity src = getSourceCompatibilitySeverity(); final Severity bin = getBinaryCompatibilitySeverity(); return src.compareTo(bin) < 0 ? bin : src; } /** * Human readable api change description. * * @return a human readable description of this API difference. */ public String getReport(MessageTranslator translator) { if (report != null) { return report; } String desc = translator.getDesc(message); int nArgs = 0; if (extraInfo != null) { nArgs = extraInfo.length; } String[] strings = new String[nArgs + 3]; strings[0] = affectedClass; strings[1] = affectedMethod; strings[2] = affectedField; for (int i = 0; i < nArgs; ++i) { strings[i + 3] = extraInfo[i]; } return java.text.MessageFormat.format(desc, strings); } /** * The fully qualified class name of the class that has changed. * @return fully qualified class name of the class that has changed. */ public String getAffectedClass() { return affectedClass; } /** * Method signature of the method that has changed, if any. * @return method signature or null if no method is affected. */ public String getAffectedMethod() { return affectedMethod; } /** * Field name of the field that has changed, if any. * @return field name or null if no field is affected. */ public String getAffectedField() { return affectedField; } /** * {@inheritDoc} */ public String toString() { StringBuffer buf = new StringBuffer(); buf.append(message.getId()); appendCommonData(buf); return buf.toString(); } /** * Get a human-readable description of this object. Intended for use by * the unit tests. */ public String toString(MessageTranslator translator) { StringBuffer buf = new StringBuffer(); buf.append(getReport(translator)); appendCommonData(buf); return buf.toString(); } /** * Build a string containing a string representation of most of the * fields in this object, but not the message-id or the string * translation thereof. */ private void appendCommonData(StringBuffer buf) { buf.append(" ("); buf.append(binaryCompatibilitySeverity); if (sourceCompatibilitySeverity != binaryCompatibilitySeverity) { buf.append(","); buf.append(sourceCompatibilitySeverity); } buf.append(") - "); buf.append(affectedClass); buf.append("["); buf.append(affectedField); buf.append("/"); buf.append(affectedMethod); buf.append("]"); } } ./clirr-0.6/core/src/java/net/sf/clirr/core/DiffListenerAdapter.java0000644000175000017500000000307510316146510025212 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core; /** * Provides empty implementations for all methods * in the DiffListener interface. * * @author lkuehne */ public class DiffListenerAdapter implements DiffListener { /** * Does nothing * @see DiffListener#reportDiff(net.sf.clirr.core.ApiDifference) */ public void reportDiff(ApiDifference difference) { } /** * Does nothing * @see DiffListener#start() */ public void start() { } /** * Does nothing * @see DiffListener#stop() */ public void stop() { } } ./clirr-0.6/core/src/java/net/sf/clirr/core/EventMessages.java0000644000175000017500000000755410316146510024112 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.core; import java.util.ResourceBundle; import java.util.Locale; import java.util.Enumeration; /** * ResourceBundle that implements the default locale by delegating to the english bundle. * This solves the bug described in * https://sourceforge.net/tracker/index.php?func=detail&aid=594469&group_id=29721&atid=397078 * without having to duplicate any resource bundles, leading to a simpler build and a smaller jar. * * @author lkuehne */ public class EventMessages extends ResourceBundle { /** * The base name of the resource bundle from which message descriptions * are read. */ public static final String RESOURCE_NAME = EventMessages.class.getName(); private ResourceBundle delegate = null; /** * Control variable used in synchronized blocks that delegate to the {@link #delegate} bundle. * The delegate bundle has "this" as it's parent. * To prevent infinite loops in the lookup process when searching for * non-existent keys we set isUsingDelegate to true to break out of the loop. */ private boolean isUsingDelegate = false; /** * Constructor. * @deprecated Typical user code never calls this directly but uses * {@link java.util.ResourceBundle#getBundle(java.lang.String)} or one of it's variants instead. */ public EventMessages() { } private ResourceBundle getDelegate() { if (delegate == null) { delegate = ResourceBundle.getBundle(RESOURCE_NAME, Locale.ENGLISH); } return delegate; } /** @see java.util.ResourceBundle#handleGetObject */ protected final synchronized Object handleGetObject(String key) { try { if (isUsingDelegate) { // the underlying bundle is delegating the call back to us // this means that the key is unknown, so we return null return null; } else { isUsingDelegate = true; return getDelegate().getObject(key); } } finally { isUsingDelegate = false; } } /** @see java.util.ResourceBundle#getKeys */ public final synchronized Enumeration getKeys() { try { if (isUsingDelegate) { // the underlying bundle is delegating the call back to us // this means that the key is unknown, so we return null return null; } else { isUsingDelegate = true; return getDelegate().getKeys(); } } finally { isUsingDelegate = false; } } /** @see java.util.ResourceBundle#getLocale */ public final Locale getLocale() { return getDelegate().getLocale(); } } ./clirr-0.6/core/src/java/net/sf/clirr/overview.html0000644000175000017500000000103210316146510022263 0ustar twernertwerner The Clirr implementation.

    WARNING: Clirr is currently in alpha state and the API is not stable.

    If you'd like to write an IDE plugin, you can use the clirr API provided here, but be aware that the API might change in the next release of Clirr.

    You should not refer to the packages labelled 'internal' in your code, though (we follow the same guidelines as Eclipse).

    ./clirr-0.6/core/src/java/net/sf/clirr/cli/0000755000175000017500000000000010316146510020302 5ustar twernertwerner./clirr-0.6/core/src/java/net/sf/clirr/cli/Clirr.java0000644000175000017500000002061110316146510022220 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2005 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// package net.sf.clirr.cli; import net.sf.clirr.core.Checker; import net.sf.clirr.core.CheckerException; import net.sf.clirr.core.ClassSelector; import net.sf.clirr.core.PlainDiffListener; import net.sf.clirr.core.XmlDiffListener; import net.sf.clirr.core.DiffListener; import net.sf.clirr.core.internal.bcel.BcelTypeArrayBuilder; import net.sf.clirr.core.spi.JavaType; import net.sf.clirr.core.spi.Scope; import org.apache.commons.cli.BasicParser; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; /** * Commandline interface for generating a difference report or checking * for binary compatibility between two versions of the same application. */ public class Clirr { public static void main(String[] args) { new Clirr().run(args); } // =================================================================== private void run(String[] args) { Options options = defineOptions(); CommandLine cmdline = parseCommandLine(args, options); String oldPath = cmdline.getOptionValue('o'); String newPath = cmdline.getOptionValue('n'); String oldClassPath = cmdline.getOptionValue("ocp"); String newClassPath = cmdline.getOptionValue("ncp"); String style = cmdline.getOptionValue('s', "text"); String outputFileName = cmdline.getOptionValue('f'); String[] includePkgs = cmdline.getOptionValues('i'); boolean showAll = cmdline.hasOption('a'); boolean showPkg = cmdline.hasOption('p'); if ((oldPath == null) || (newPath == null)) { usage(options); System.exit(-1); } Checker checker = new Checker(); if (showAll) { checker.getScopeSelector().setScope(Scope.PRIVATE); } else if (showPkg) { checker.getScopeSelector().setScope(Scope.PACKAGE); } ClassSelector classSelector; if ((includePkgs != null) && (includePkgs.length > 0)) { classSelector = new ClassSelector(ClassSelector.MODE_IF); for (int i = 0; i < includePkgs.length; ++i) { classSelector.addPackageTree(includePkgs[i]); } } else { // a selector that selects everything classSelector = new ClassSelector(ClassSelector.MODE_UNLESS); } DiffListener diffListener = null; if (style.equals("text")) { try { diffListener = new PlainDiffListener(outputFileName); } catch (IOException ex) { System.err.println("Invalid output file name."); } } else if (style.equals("xml")) { try { diffListener = new XmlDiffListener(outputFileName); } catch (IOException ex) { System.err.println("Invalid output file name."); } } else { System.err.println("Invalid style option. Must be one of 'text', 'xml'."); usage(options); System.exit(-1); } File[] origJars = pathToFileArray(oldPath); File[] newJars = pathToFileArray(newPath); checker.addDiffListener(diffListener); try { ClassLoader loader1 = new URLClassLoader(convertFilesToURLs(pathToFileArray(oldClassPath))); ClassLoader loader2 = new URLClassLoader(convertFilesToURLs(pathToFileArray(newClassPath))); final JavaType[] origClasses = BcelTypeArrayBuilder.createClassSet(origJars, loader1, classSelector); final JavaType[] newClasses = BcelTypeArrayBuilder.createClassSet(newJars, loader2, classSelector); checker.reportDiffs(origClasses, newClasses); System.exit(0); } catch (CheckerException ex) { System.err.println("Unable to complete checks:" + ex.getMessage()); System.exit(1); } catch (MalformedURLException ex) { System.err.println("Unable to create classloader for 3rd party classes:" + ex.getMessage()); System.err.println("old classpath: " + oldClassPath); System.err.println("new classpath: " + newClassPath); System.exit(1); } } /** * @param args * @param parser * @param options * @return */ private CommandLine parseCommandLine(String[] args, Options options) { BasicParser parser = new BasicParser(); CommandLine cmdline = null; try { cmdline = parser.parse(options, args); } catch (ParseException ex) { System.err.println("Invalid command line arguments."); usage(options); System.exit(-1); } return cmdline; } /** * @return */ private Options defineOptions() { Options options = new Options(); options.addOption("o", "old-version", true, "jar files of old version"); options.addOption("n", "new-version", true, "jar files of new version"); options.addOption("ocp", "orig-classpath", true, "3rd party classpath that is referenced by old-version"); options.addOption("ncp", "new-classpath", true, "3rd party classpath that is referenced by new-version"); options.addOption("s", "style", true, "output style [text|xml]"); options.addOption("i", "include-pkg", true, "include only classes from this package and its subpackages"); options.addOption("p", "show-pkg-scope", false, "show package scope classes"); options.addOption("a", "show-all-scopes", false, "show private and package classes"); options.addOption("f", "output-file", true, "output file name"); return options; } private void usage(Options options) { HelpFormatter hf = new HelpFormatter(); PrintWriter out = new PrintWriter(System.err); hf.printHelp( 75, "java " + getClass().getName() + " -o path -n path [options]", null, options, null); } private File[] pathToFileArray(String path) { if (path == null) { return new File[0]; } ArrayList files = new ArrayList(); int pos = 0; while (pos < path.length()) { int colonPos = path.indexOf(pos, File.pathSeparatorChar); if (colonPos == -1) { files.add(new File(path.substring(pos))); break; } files.add(new File(path.substring(pos, colonPos))); pos = colonPos + 1; } return (File[]) files.toArray(new File[files.size()]); } private URL[] convertFilesToURLs(File[] files) throws MalformedURLException { URL[] ret = new URL[files.length]; for (int i = 0; i < files.length; i++) { ret[i] = files[i].toURL(); } return ret; } } ./clirr-0.6/core/src/java/net/sf/clirr/cli/package.html0000644000175000017500000000007710316146510022567 0ustar twernertwerner Command line frontend for Clirr. ./clirr-0.6/core/project.xml0000644000175000017500000000460610316146510015710 0ustar twernertwerner ${basedir}/../project.xml clirr-core Clirr Core net.sf.clirr scm:cvs:pserver:anonymous@cvs.sourceforge.net:/cvsroot/clirr:clirr/core http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/clirr/core bcel bcel 5.1 http://jakarta.apache.org/bcel ant ant 1.5.3-1 http://ant.apache.org false commons-cli commons-cli 1.0 http://jakarta.apache.org/commons-cli commons-lang commons-lang 1.0.1 http://jakarta.apache.org/commons-lang maven-plugins maven-javaapp-plugin 1.3.1 http://maven-plugins.sourceforge.org/maven-javaapp-plugin/ plugin clirr-devel@lists.sourceforge.net ${basedir}/src/java ${basedir}/src/test **/*Test.java ${basedir}/src/conf maven-changes-plugin maven-javadoc-plugin maven-jdepend-plugin maven-junit-report-plugin maven-jxr-plugin maven-license-plugin maven-tasklist-plugin ./clirr-0.6/LICENSE.LGPL0000644000175000017500000006261710316146510014340 0ustar twernertwerner GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. ./clirr-0.6/LICENSE.txt0000644000175000017500000000145110316146510014406 0ustar twernertwernerClirr: compares two versions of a java library for binary compatibility Copyright (C) 2003 - 2005 Lars Khne This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ./clirr-0.6/conf/0000755000175000017500000000000010316146510013507 5ustar twernertwerner./clirr-0.6/conf/javaheader.txt0000644000175000017500000000176510316146510016353 0ustar twernertwerner////////////////////////////////////////////////////////////////////////////// // Clirr: compares two versions of a java library for binary compatibility // Copyright (C) 2003 - 2004 Lars Khne // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ////////////////////////////////////////////////////////////////////////////// ./clirr-0.6/conf/clirr_checks.xml0000644000175000017500000001612510316146510016671 0ustar twernertwerner ./clirr-0.6/project.xml0000644000175000017500000001236610316146510014762 0ustar twernertwerner 3 net.sf.clirr clirr Clirr 0.6 Lars Khne mailto:clirr-devel@lists.sourceforge.net 2003 net.sf.clirr /images/clirr.png Clirr is a tool that checks Java libraries for binary compatibility with older releases. Basically you give it two sets of jar files and Clirr dumps out a list of changes in the public api. Clirr provides an Ant task that can be configured to break the build if it detects incompatible api changes. In a continuous integration process Clirr can automatically prevent accidental introduction of binary compatibility problems in Java libraries. Java binary compatibility checker http://clirr.sourceforge.net/ https://sourceforge.net/tracker/?group_id=89627&atid=590799 shell.sourceforge.net /home/groups/c/cl/clirr/htdocs/ shell.sourceforge.net /home/groups/c/cl/clirr/htdocs/builds/ scm:cvs:pserver:anonymous@cvs.sourceforge.net:/cvsroot/clirr:clirr http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/clirr 0.2 0.2 RELEASE_CLIRR_0_2 0.3 0.3 RELEASE_CLIRR_0_3 0.4 0.4 RELEASE_CLIRR_0_4 0.5 0.5 RELEASE_CLIRR_0_5 clirr-user http://lists.sourceforge.net/lists/listinfo/clirr-user http://lists.sourceforge.net/lists/listinfo/clirr-user http://lists.sourceforge.net/lists/listinfo/clirr-user clirr-devel http://lists.sourceforge.net/lists/listinfo/clirr-devel http://lists.sourceforge.net/lists/listinfo/clirr-devel http://lists.sourceforge.net/lists/listinfo/clirr-devel Lars Khne lkuehne ppi Media +1 Java Developer Project Admin Vincent Massol vmassol Apache Software Foundation +1 http://blogs.codehaus.org/people/vmassol Initial Inspiration and Feedback Java Developer Maven Guru Simon Kitching s_kitching ECN +12 Java Developer Stephen Colebourne Apache Software Foundation Feedback Bug reports Usability testing clirr-devel@lists.sourceforge.net maven-multichanges-plugin maven-license-plugin maven-tasklist-plugin