This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to annotated tag japicmp-base-0.0.2 in repository https://gitbox.apache.org/repos/asf/commons-vfs.git
commit 53a32f8f836bc6e2cb573b4404d9ec58f1ee67a9 Author: siom79 <[email protected]> AuthorDate: Thu Oct 3 22:15:56 2013 +0200 added -a option that lets the user specify the access modifier level used to compare classes and class members --- README.md | 47 ++++++++++++---------- .../src/main/java/japicmp/test/Modifier.java | 12 ++++++ .../src/main/java/japicmp/test/Modifier.java | 12 ++++++ .../src/test/java/japicmp/test/BasicTest.java | 29 ++----------- .../src/test/java/japicmp/test/ModifierTest.java | 44 ++++++++++++++++++++ .../src/test/java/japicmp/test/util/Helper.java | 32 +++++++++++++++ japicmp/src/main/java/japicmp/cli/CliParser.java | 26 +++++++++++- .../src/main/java/japicmp/cmp/AccessModifier.java | 15 +++++++ .../src/main/java/japicmp/cmp/ClassComparator.java | 18 ++++++--- .../main/java/japicmp/cmp/ClassesComparator.java | 8 ++-- .../java/japicmp/cmp/JarArchiveComparator.java | 21 ++++++---- .../japicmp/cmp/JarArchiveComparatorOptions.java | 9 +++++ japicmp/src/main/java/japicmp/config/Options.java | 10 +++++ japicmp/src/main/java/japicmp/model/JApiClass.java | 23 ++++++++++- .../src/main/java/japicmp/model/JApiMethod.java | 23 ++++++++++- .../output/stdout/StdoutOutputGenerator.java | 2 +- .../src/main/java/japicmp/util/ModifierHelper.java | 29 +++++++++++++ .../test/java/japicmp/util/ModifierHelperTest.java | 41 +++++++++++++++++++ 18 files changed, 334 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index 31554de..27f33b2 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ japicmp japicmp is a tool to compare two versions of a jar archive: - java -jar japicmp-0.0.1-SNAPSHOT.jar -n new-version.jar -o old-version.jar + java -jar japicmp-0.0.1.jar -n new-version.jar -o old-version.jar It can also be used as a library to integrate its functionality in some other kind of software: @@ -24,9 +24,11 @@ library to inspect the class files. This way you only have to provide the two ja ##Features## -* Comparison of two jar archives without the need to add all of their dependencies to the classpath +* Comparison of two jar archives without the need to add all of their dependencies to the classpath. * Differences are printed on the command line in a simple diff format. * Differences can optionally be printed to an xml file. This can be transformed to an HTML file using XSLT. +* Per default only public classes and class members are compared. If necessary, the access modifier of the classes and class members to be + compared can be set to package, protected or private. ##Usage## @@ -36,31 +38,34 @@ The tool has a set of CLI parameters that are described in the following: -o <pathToOldVersionJar> Provides the path to the old version of the jar. -n <pathToNewVersionJar> Provides the path to the new version of the jar. -x <pathToXmlOutputFile> Provides the path to the xml output file. If not given, stdout is used. + -a <accessModifier> Sets the access modifier level (public, package, protected, private), which should be used. -m Outputs only modified classes/methods. If not given, all classes and methods are printed. ###Example### In the following you see the beginning of the xml output file after having computed the differences between the versions 4.0.1 and 4.2.3 of httpclient: - <?xml version="1.0" encoding="UTF-8" standalone="yes"?> - <japicmp newJar="D:\Programmierung\japicmp\github\japicmp\japicmp\httpclient-4.2.3.jar" oldJar="D:\Programmierung\japicmp\github\japicmp\japicmp\httpclient-4.0.1.jar"> - <class changeStatus="REMOVED" fullyQualifiedName="org.apache.http.annotation.GuardedBy" type="ANNOTATION"> - <method changeStatus="REMOVED" name="value" returnType="java.lang.String"/> - </class> - <class changeStatus="REMOVED" fullyQualifiedName="org.apache.http.annotation.Immutable" type="ANNOTATION"/> - <class changeStatus="REMOVED" fullyQualifiedName="org.apache.http.annotation.NotThreadSafe" type="ANNOTATION"/> - <class changeStatus="REMOVED" fullyQualifiedName="org.apache.http.annotation.ThreadSafe" type="ANNOTATION"/> - <class changeStatus="NEW" fullyQualifiedName="org.apache.http.auth.AuthOption" type="CLASS"> - <method changeStatus="NEW" name="getAuthScheme" returnType="org.apache.http.auth.AuthScheme"/> - <method changeStatus="NEW" name="getCredentials" returnType="org.apache.http.auth.Credentials"/> - <method changeStatus="NEW" name="toString" returnType="java.lang.String"/> - </class> - <class changeStatus="NEW" fullyQualifiedName="org.apache.http.auth.AuthProtocolState" type="ENUM"> - <method changeStatus="NEW" name="valueOf" returnType="org.apache.http.auth.AuthProtocolState"> - <parameter type="java.lang.String"/> - </method> - <method changeStatus="NEW" name="values" returnType="org.apache.http.auth.AuthProtocolState[]"/> - </class> + <?xml version="1.0" encoding="UTF-8" standalone="yes"?> + <japicmp newJar="D:\Programmierung\japicmp\github\japicmp\japicmp\httpclient-4.2.3.jar" oldJar="D:\Programmierung\japicmp\github\japicmp\japicmp\httpclient-4.0.1.jar"> + <class accessModifierNew="n.a." accessModifierOld="PUBLIC" changeStatus="REMOVED" fullyQualifiedName="org.apache.http.annotation.GuardedBy" type="ANNOTATION"> + <method accessModifierNew="n.a." accessModifierOld="PUBLIC" changeStatus="REMOVED" name="value" returnType="java.lang.String"/> + </class> + <class accessModifierNew="n.a." accessModifierOld="PUBLIC" changeStatus="REMOVED" fullyQualifiedName="org.apache.http.annotation.Immutable" type="ANNOTATION"/> + <class accessModifierNew="n.a." accessModifierOld="PUBLIC" changeStatus="REMOVED" fullyQualifiedName="org.apache.http.annotation.NotThreadSafe" type="ANNOTATION"/> + <class accessModifierNew="n.a." accessModifierOld="PUBLIC" changeStatus="REMOVED" fullyQualifiedName="org.apache.http.annotation.ThreadSafe" type="ANNOTATION"/> + <class accessModifierNew="PUBLIC" accessModifierOld="PUBLIC" changeStatus="UNCHANGED" fullyQualifiedName="org.apache.http.auth.AUTH" type="CLASS"/> + <class accessModifierNew="PUBLIC" accessModifierOld="PUBLIC" changeStatus="UNCHANGED" fullyQualifiedName="org.apache.http.auth.AuthenticationException" type="CLASS"/> + <class accessModifierNew="PUBLIC" accessModifierOld="n.a." changeStatus="NEW" fullyQualifiedName="org.apache.http.auth.AuthOption" type="CLASS"> + <method accessModifierNew="PUBLIC" accessModifierOld="n.a." changeStatus="NEW" name="getAuthScheme" returnType="org.apache.http.auth.AuthScheme"/> + <method accessModifierNew="PUBLIC" accessModifierOld="n.a." changeStatus="NEW" name="getCredentials" returnType="org.apache.http.auth.Credentials"/> + <method accessModifierNew="PUBLIC" accessModifierOld="n.a." changeStatus="NEW" name="toString" returnType="java.lang.String"/> + </class> + <class accessModifierNew="PUBLIC" accessModifierOld="n.a." changeStatus="NEW" fullyQualifiedName="org.apache.http.auth.AuthProtocolState" type="ENUM"> + <method accessModifierNew="PUBLIC" accessModifierOld="n.a." changeStatus="NEW" name="valueOf" returnType="org.apache.http.auth.AuthProtocolState"> + <parameter type="java.lang.String"/> + </method> + <method accessModifierNew="PUBLIC" accessModifierOld="n.a." changeStatus="NEW" name="values" returnType="org.apache.http.auth.AuthProtocolState[]"/> + </class> ... ##Downloads## diff --git a/japicmp-testbase/japicmp-test-v1/src/main/java/japicmp/test/Modifier.java b/japicmp-testbase/japicmp-test-v1/src/main/java/japicmp/test/Modifier.java new file mode 100644 index 0000000..b53a7bf --- /dev/null +++ b/japicmp-testbase/japicmp-test-v1/src/main/java/japicmp/test/Modifier.java @@ -0,0 +1,12 @@ +package japicmp.test; + +public class Modifier { + + public void publicToPrivateMethod() { + + } + + public static class ModifierPublicToProtected { + + } +} diff --git a/japicmp-testbase/japicmp-test-v2/src/main/java/japicmp/test/Modifier.java b/japicmp-testbase/japicmp-test-v2/src/main/java/japicmp/test/Modifier.java new file mode 100644 index 0000000..65bf223 --- /dev/null +++ b/japicmp-testbase/japicmp-test-v2/src/main/java/japicmp/test/Modifier.java @@ -0,0 +1,12 @@ +package japicmp.test; + +public class Modifier { + + private void publicToPrivateMethod() { + + } + + private static class ModifierPublicToProtected { + + } +} diff --git a/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/BasicTest.java b/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/BasicTest.java index 7abab9b..c02ea3a 100644 --- a/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/BasicTest.java +++ b/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/BasicTest.java @@ -4,12 +4,13 @@ import japicmp.cmp.JarArchiveComparator; import japicmp.cmp.JarArchiveComparatorOptions; import japicmp.model.JApiChangeStatus; import japicmp.model.JApiClass; -import japicmp.model.JApiMethod; import org.junit.Test; -import java.io.File; import java.util.List; +import static japicmp.test.util.Helper.getArchive; +import static japicmp.test.util.Helper.getJApiClass; +import static japicmp.test.util.Helper.getJApiMethod; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertThat; @@ -20,7 +21,7 @@ public class BasicTest { public void test() { JarArchiveComparator jarArchiveComparator = new JarArchiveComparator(new JarArchiveComparatorOptions()); List<JApiClass> jApiClasses = jarArchiveComparator.compare(getArchive("japicmp-test-v1.jar"), getArchive("japicmp-test-v2.jar")); - assertThat(jApiClasses.size(), is(3)); + assertThat(jApiClasses.size(), is(5)); JApiClass jApiClassRemoved = getJApiClass(jApiClasses, Removed.class.getName()); JApiClass jApiClassAdded = getJApiClass(jApiClasses, Added.class.getName()); JApiClass jApiClassUnchanged = getJApiClass(jApiClasses, Unchanged.class.getName()); @@ -32,26 +33,4 @@ public class BasicTest { assertThat(jApiClassUnchanged.getChangeStatus(), is(JApiChangeStatus.UNCHANGED)); assertThat(getJApiMethod(jApiClassUnchanged.getMethods(), "unchangedMethod"), is(notNullValue())); } - - private File getArchive(String filename) { - return new File("target" + File.separator + filename); - } - - private JApiClass getJApiClass(List<JApiClass> jApiClasses, String fqn) { - for (JApiClass jApiClass : jApiClasses) { - if (jApiClass.getFullyQualifiedName().equals(fqn)) { - return jApiClass; - } - } - throw new IllegalArgumentException("No class found with name " + fqn + "."); - } - - private JApiMethod getJApiMethod(List<JApiMethod> jApiMethods, String name) { - for(JApiMethod jApiMethod : jApiMethods) { - if(jApiMethod.getName().equals(name)) { - return jApiMethod; - } - } - throw new IllegalArgumentException("No method found with name " + name + "."); - } } diff --git a/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/ModifierTest.java b/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/ModifierTest.java new file mode 100644 index 0000000..f939228 --- /dev/null +++ b/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/ModifierTest.java @@ -0,0 +1,44 @@ +package japicmp.test; + +import japicmp.cmp.AccessModifier; +import japicmp.cmp.JarArchiveComparator; +import japicmp.cmp.JarArchiveComparatorOptions; +import japicmp.model.JApiChangeStatus; +import japicmp.model.JApiClass; +import japicmp.test.util.Helper; +import org.junit.Test; + +import java.util.List; + +import static japicmp.test.util.Helper.getArchive; +import static japicmp.test.util.Helper.getJApiClass; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; + +public class ModifierTest { + + @Test + public void testOptionPublicModifier() { + JarArchiveComparatorOptions options = new JarArchiveComparatorOptions(); + options.setModifierLevel(AccessModifier.PUBLIC); + JarArchiveComparator jarArchiveComparator = new JarArchiveComparator(options); + List<JApiClass> jApiClasses = jarArchiveComparator.compare(getArchive("japicmp-test-v1.jar"), getArchive("japicmp-test-v2.jar")); + JApiClass modifierInnerClass = getJApiClass(jApiClasses, Modifier.ModifierPublicToProtected.class.getName()); + JApiClass modifierClass = getJApiClass(jApiClasses, Modifier.class.getName()); + assertThat(modifierInnerClass.getChangeStatus(), is(JApiChangeStatus.REMOVED)); + assertThat(Helper.getJApiMethod(modifierClass.getMethods(), "publicToPrivateMethod").getChangeStatus(), is(JApiChangeStatus.REMOVED)); + } + + @Test + public void testOptionPrivateModifier() { + JarArchiveComparatorOptions options = new JarArchiveComparatorOptions(); + options.setModifierLevel(AccessModifier.PRIVATE); + JarArchiveComparator jarArchiveComparator = new JarArchiveComparator(options); + List<JApiClass> jApiClasses = jarArchiveComparator.compare(getArchive("japicmp-test-v1.jar"), getArchive("japicmp-test-v2.jar")); + JApiClass modifierInnerClass = getJApiClass(jApiClasses, Modifier.ModifierPublicToProtected.class.getName()); + JApiClass modifierClass = getJApiClass(jApiClasses, Modifier.class.getName()); + assertThat(modifierInnerClass.getChangeStatus(), is(JApiChangeStatus.UNCHANGED)); + assertThat(Helper.getJApiMethod(modifierClass.getMethods(), "publicToPrivateMethod").getChangeStatus(), is(JApiChangeStatus.UNCHANGED)); + } +} diff --git a/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/util/Helper.java b/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/util/Helper.java new file mode 100644 index 0000000..c98da86 --- /dev/null +++ b/japicmp-testbase/japicmp-test/src/test/java/japicmp/test/util/Helper.java @@ -0,0 +1,32 @@ +package japicmp.test.util; + +import japicmp.model.JApiClass; +import japicmp.model.JApiMethod; + +import java.io.File; +import java.util.List; + +public class Helper { + + public static File getArchive(String filename) { + return new File("target" + File.separator + filename); + } + + public static JApiClass getJApiClass(List<JApiClass> jApiClasses, String fqn) { + for (JApiClass jApiClass : jApiClasses) { + if (jApiClass.getFullyQualifiedName().equals(fqn)) { + return jApiClass; + } + } + throw new IllegalArgumentException("No class found with name " + fqn + "."); + } + + public static JApiMethod getJApiMethod(List<JApiMethod> jApiMethods, String name) { + for(JApiMethod jApiMethod : jApiMethods) { + if(jApiMethod.getName().equals(name)) { + return jApiMethod; + } + } + throw new IllegalArgumentException("No method found with name " + name + "."); + } +} diff --git a/japicmp/src/main/java/japicmp/cli/CliParser.java b/japicmp/src/main/java/japicmp/cli/CliParser.java index 1e1f788..db1e287 100644 --- a/japicmp/src/main/java/japicmp/cli/CliParser.java +++ b/japicmp/src/main/java/japicmp/cli/CliParser.java @@ -1,6 +1,7 @@ package japicmp.cli; import com.google.common.base.Optional; +import japicmp.cmp.AccessModifier; import japicmp.config.Options; import japicmp.util.StringArrayEnumeration; @@ -32,9 +33,19 @@ public class CliParser { System.out.println("-o <pathToOldVersionJar> Provides the path to the old version of the jar."); System.out.println("-n <pathToNewVersionJar> Provides the path to the new version of the jar."); System.out.println("-x <pathToXmlOutputFile> Provides the path to the xml output file. If not given, stdout is used."); + System.out.println("-a <accessModifier> Sets the access modifier level (public, package, protected, private), which should be used."); System.out.println("-m Outputs only modified classes/methods. If not given, all classes and methods are printed."); System.exit(0); } + if ("-a".equals(arg)) { + String accessModifierArg = getOptionWithArgument("-a", sae); + try { + AccessModifier accessModifier = AccessModifier.valueOf(accessModifierArg.toUpperCase()); + options.setAcessModifier(accessModifier); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException(String.format("Invalid value for option -a: %s. Possible values are: %s.", accessModifierArg, listOfAccessModifiers())); + } + } } checkForMandatoryOptions(options); return options; @@ -52,7 +63,7 @@ public class CliParser { private String getOptionWithArgument(String option, StringArrayEnumeration sae) { if (sae.hasMoreElements()) { String value = sae.nextElement(); - if(value.startsWith("-")) { + if (value.startsWith("-")) { throw new IllegalArgumentException(String.format("Missing argument for option %s.", option)); } return value; @@ -60,4 +71,17 @@ public class CliParser { throw new IllegalArgumentException(String.format("Missing argument for option %s.", option)); } } + + private String listOfAccessModifiers() { + StringBuilder sb = new StringBuilder(); + int i = 0; + for (AccessModifier am : AccessModifier.values()) { + if (i > 0) { + sb.append(","); + } + sb.append(am.toString()); + i++; + } + return sb.toString(); + } } diff --git a/japicmp/src/main/java/japicmp/cmp/AccessModifier.java b/japicmp/src/main/java/japicmp/cmp/AccessModifier.java new file mode 100644 index 0000000..8f9ffec --- /dev/null +++ b/japicmp/src/main/java/japicmp/cmp/AccessModifier.java @@ -0,0 +1,15 @@ +package japicmp.cmp; + +public enum AccessModifier { + PUBLIC(3), PACKAGE(2), PROTECTED(1), PRIVATE(0); + + private int level; + + AccessModifier(int level) { + this.level = level; + } + + public int getLevel() { + return level; + } +} diff --git a/japicmp/src/main/java/japicmp/cmp/ClassComparator.java b/japicmp/src/main/java/japicmp/cmp/ClassComparator.java index 92838e7..0bcb783 100644 --- a/japicmp/src/main/java/japicmp/cmp/ClassComparator.java +++ b/japicmp/src/main/java/japicmp/cmp/ClassComparator.java @@ -5,6 +5,7 @@ import japicmp.model.JApiChangeStatus; import japicmp.model.JApiClass; import japicmp.model.JApiMethod; import japicmp.model.JApiParameter; +import japicmp.util.ModifierHelper; import japicmp.util.SignatureParser; import javassist.CtClass; import javassist.CtMethod; @@ -13,6 +14,11 @@ import java.util.HashMap; import java.util.Map; public class ClassComparator { + private final JarArchiveComparatorOptions options; + + public ClassComparator(JarArchiveComparatorOptions options) { + this.options = options; + } public void compare(JApiClass jApiClass) { Map<String, CtMethod> oldMethodsMap = createMethodMap(jApiClass.getOldClass()); @@ -27,14 +33,14 @@ public class ClassComparator { signatureParser.parse(ctMethod.getSignature()); CtMethod foundMethod = newMethodsMap.get(longName); if (foundMethod == null) { - JApiMethod jApiMethod = new JApiMethod(ctMethod.getName(), JApiChangeStatus.REMOVED, Optional.of(ctMethod), Optional.<CtMethod>absent(), signatureParser.getReturnType()); + JApiMethod jApiMethod = new JApiMethod(ctMethod.getName(), JApiChangeStatus.REMOVED, Optional.of(ctMethod), Optional.<CtMethod>absent(), signatureParser.getReturnType(), Optional.of(ModifierHelper.translateToModifierLevel(ctMethod.getModifiers())), Optional.<AccessModifier>absent()); addParametersToMethod(signatureParser, jApiMethod); jApiClass.addMethod(jApiMethod); if(jApiClass.getChangeStatus() == JApiChangeStatus.UNCHANGED) { jApiClass.setChangeStatus(JApiChangeStatus.MODIFIED); } } else { - JApiMethod jApiMethod = new JApiMethod(ctMethod.getName(), JApiChangeStatus.UNCHANGED, Optional.of(ctMethod), Optional.of(foundMethod), signatureParser.getReturnType()); + JApiMethod jApiMethod = new JApiMethod(ctMethod.getName(), JApiChangeStatus.UNCHANGED, Optional.of(ctMethod), Optional.of(foundMethod), signatureParser.getReturnType(), Optional.of(ModifierHelper.translateToModifierLevel(ctMethod.getModifiers())), Optional.of(ModifierHelper.translateToModifierLevel(foundMethod.getModifiers()))); addParametersToMethod(signatureParser, jApiMethod); jApiClass.addMethod(jApiMethod); } @@ -44,7 +50,7 @@ public class ClassComparator { signatureParser.parse(ctMethod.getSignature()); CtMethod foundMethod = oldMethodsMap.get(longName); if (foundMethod == null) { - JApiMethod jApiMethod = new JApiMethod(ctMethod.getName(), JApiChangeStatus.NEW, Optional.<CtMethod>absent(), Optional.of(ctMethod), signatureParser.getReturnType()); + JApiMethod jApiMethod = new JApiMethod(ctMethod.getName(), JApiChangeStatus.NEW, Optional.<CtMethod>absent(), Optional.of(ctMethod), signatureParser.getReturnType(), Optional.<AccessModifier>absent(), Optional.of(ModifierHelper.translateToModifierLevel(ctMethod.getModifiers()))); addParametersToMethod(signatureParser, jApiMethod); jApiClass.addMethod(jApiMethod); if(jApiClass.getChangeStatus() == JApiChangeStatus.UNCHANGED) { @@ -63,8 +69,10 @@ public class ClassComparator { private Map<String, CtMethod> createMethodMap(Optional<CtClass> ctClass) { Map<String, CtMethod> methods = new HashMap<String, CtMethod>(); if (ctClass.isPresent()) { - for (CtMethod ctMethod : ctClass.get().getMethods()) { - methods.put(ctMethod.getLongName(), ctMethod); + for (CtMethod ctMethod : ctClass.get().getDeclaredMethods()) { + if(ModifierHelper.matchesModifierLevel(ctMethod.getModifiers(), options.getModifierLevel())) { + methods.put(ctMethod.getLongName(), ctMethod); + } } } return methods; diff --git a/japicmp/src/main/java/japicmp/cmp/ClassesComparator.java b/japicmp/src/main/java/japicmp/cmp/ClassesComparator.java index ee02733..b8e2b48 100644 --- a/japicmp/src/main/java/japicmp/cmp/ClassesComparator.java +++ b/japicmp/src/main/java/japicmp/cmp/ClassesComparator.java @@ -3,8 +3,8 @@ package japicmp.cmp; import com.google.common.base.Optional; import japicmp.model.JApiChangeStatus; import japicmp.model.JApiClass; +import japicmp.util.ModifierHelper; import javassist.CtClass; -import javassist.Modifier; import java.util.HashMap; import java.util.LinkedList; @@ -25,15 +25,15 @@ public class ClassesComparator { for(CtClass ctClass : oldClassesMap.values()) { CtClass foundClass = newClassesMap.get(ctClass.getName()); if(foundClass == null) { - classes.add(new JApiClass(ctClass.getName(), Optional.<CtClass>of(ctClass), Optional.<CtClass>absent(), JApiChangeStatus.REMOVED, getType(ctClass))); + classes.add(new JApiClass(ctClass.getName(), Optional.<CtClass>of(ctClass), Optional.<CtClass>absent(), JApiChangeStatus.REMOVED, getType(ctClass), Optional.of(ModifierHelper.translateToModifierLevel(ctClass.getModifiers())), Optional.<AccessModifier>absent())); } else { - classes.add(new JApiClass(ctClass.getName(), Optional.<CtClass>of(ctClass), Optional.<CtClass>of(foundClass), JApiChangeStatus.UNCHANGED, getType(ctClass))); + classes.add(new JApiClass(ctClass.getName(), Optional.<CtClass>of(ctClass), Optional.<CtClass>of(foundClass), JApiChangeStatus.UNCHANGED, getType(ctClass), Optional.of(ModifierHelper.translateToModifierLevel(ctClass.getModifiers())), Optional.of(ModifierHelper.translateToModifierLevel(foundClass.getModifiers())))); } } for(CtClass ctClass : newClassesMap.values()) { CtClass foundClass = oldClassesMap.get(ctClass.getName()); if(foundClass == null) { - classes.add(new JApiClass(ctClass.getName(), Optional.<CtClass>absent(), Optional.<CtClass>of(ctClass), JApiChangeStatus.NEW, getType(ctClass))); + classes.add(new JApiClass(ctClass.getName(), Optional.<CtClass>absent(), Optional.<CtClass>of(ctClass), JApiChangeStatus.NEW, getType(ctClass), Optional.<AccessModifier>absent(), Optional.of(ModifierHelper.translateToModifierLevel(ctClass.getModifiers())))); } } } diff --git a/japicmp/src/main/java/japicmp/cmp/JarArchiveComparator.java b/japicmp/src/main/java/japicmp/cmp/JarArchiveComparator.java index 06097d1..3dbdda4 100644 --- a/japicmp/src/main/java/japicmp/cmp/JarArchiveComparator.java +++ b/japicmp/src/main/java/japicmp/cmp/JarArchiveComparator.java @@ -1,8 +1,10 @@ package japicmp.cmp; import japicmp.model.JApiClass; +import japicmp.util.ModifierHelper; import javassist.ClassPool; import javassist.CtClass; +import javassist.Modifier; import org.apache.log4j.Logger; import java.io.File; @@ -14,15 +16,16 @@ import java.util.jar.JarFile; public class JarArchiveComparator { private static final Logger logger = Logger.getLogger(JarArchiveComparator.class); + private JarArchiveComparatorOptions options; public JarArchiveComparator(JarArchiveComparatorOptions options) { - + this.options = options; } public List<JApiClass> compare(File oldArchive, File newArchive) { ClassPool classPool = new ClassPool(); try { - ClassesComparator classesComparator = compareClassLists(oldArchive, newArchive, classPool); + ClassesComparator classesComparator = compareClassLists(oldArchive, newArchive, classPool, options); List<JApiClass> classList = classesComparator.getClasses(); compareClasses(classList); return classList; @@ -34,14 +37,14 @@ public class JarArchiveComparator { private void compareClasses(List<JApiClass> classList) { for (JApiClass jApiClass : classList) { - ClassComparator classComparator = new ClassComparator(); + ClassComparator classComparator = new ClassComparator(options); classComparator.compare(jApiClass); } } - private ClassesComparator compareClassLists(File oldArchive, File newArchive, ClassPool classPool) throws Exception { - List<CtClass> oldClasses = createListOfCtClasses(oldArchive, classPool); - List<CtClass> newClasses = createListOfCtClasses(newArchive, classPool); + private ClassesComparator compareClassLists(File oldArchive, File newArchive, ClassPool classPool, JarArchiveComparatorOptions options) throws Exception { + List<CtClass> oldClasses = createListOfCtClasses(oldArchive, classPool, options); + List<CtClass> newClasses = createListOfCtClasses(newArchive, classPool, options); ClassesComparator classesComparator = new ClassesComparator(); classesComparator.compare(oldClasses, newClasses); if (logger.isDebugEnabled()) { @@ -52,7 +55,7 @@ public class JarArchiveComparator { return classesComparator; } - private List<CtClass> createListOfCtClasses(File archive, ClassPool classPool) throws Exception { + private List<CtClass> createListOfCtClasses(File archive, ClassPool classPool, JarArchiveComparatorOptions options) throws Exception { List<CtClass> classes = new LinkedList<CtClass>(); JarFile oldJar = null; try { @@ -69,7 +72,9 @@ public class JarArchiveComparator { logger.error(String.format("Failed to load file from jar '%s' as class file: %s.", name, e.getMessage())); throw e; } - classes.add(ctClass); + if(ModifierHelper.matchesModifierLevel(ctClass.getModifiers(), options.getModifierLevel())) { + classes.add(ctClass); + } if (logger.isDebugEnabled()) { logger.debug(String.format("Adding class '%s' with jar name '%s' to list.", ctClass.getName(), name)); } diff --git a/japicmp/src/main/java/japicmp/cmp/JarArchiveComparatorOptions.java b/japicmp/src/main/java/japicmp/cmp/JarArchiveComparatorOptions.java index c474ef6..cd2a328 100644 --- a/japicmp/src/main/java/japicmp/cmp/JarArchiveComparatorOptions.java +++ b/japicmp/src/main/java/japicmp/cmp/JarArchiveComparatorOptions.java @@ -6,6 +6,7 @@ import java.util.List; public class JarArchiveComparatorOptions { private List<String> packagesInclude = new LinkedList<String>(); private List<String> packagesExclude = new LinkedList<String>(); + private AccessModifier modifierLevel = AccessModifier.PUBLIC; public List<String> getPackagesExclude() { return packagesExclude; @@ -14,4 +15,12 @@ public class JarArchiveComparatorOptions { public List<String> getPackagesInclude() { return packagesInclude; } + + public AccessModifier getModifierLevel() { + return modifierLevel; + } + + public void setModifierLevel(AccessModifier modifierLevel) { + this.modifierLevel = modifierLevel; + } } diff --git a/japicmp/src/main/java/japicmp/config/Options.java b/japicmp/src/main/java/japicmp/config/Options.java index 4576f72..6337fac 100644 --- a/japicmp/src/main/java/japicmp/config/Options.java +++ b/japicmp/src/main/java/japicmp/config/Options.java @@ -1,12 +1,14 @@ package japicmp.config; import com.google.common.base.Optional; +import japicmp.cmp.AccessModifier; public class Options { private String oldArchive; private String newArchive; private boolean outputOnlyModifications = false; private Optional<String> xmlOutputFile = Optional.<String>absent(); + private AccessModifier acessModifier = AccessModifier.PUBLIC; public String getNewArchive() { return newArchive; @@ -39,4 +41,12 @@ public class Options { public void setXmlOutputFile(Optional<String> xmlOutputFile) { this.xmlOutputFile = xmlOutputFile; } + + public void setAcessModifier(AccessModifier acessModifier) { + this.acessModifier = acessModifier; + } + + public AccessModifier getAcessModifier() { + return acessModifier; + } } diff --git a/japicmp/src/main/java/japicmp/model/JApiClass.java b/japicmp/src/main/java/japicmp/model/JApiClass.java index e1d429f..242b6c3 100644 --- a/japicmp/src/main/java/japicmp/model/JApiClass.java +++ b/japicmp/src/main/java/japicmp/model/JApiClass.java @@ -1,6 +1,7 @@ package japicmp.model; import com.google.common.base.Optional; +import japicmp.cmp.AccessModifier; import javassist.CtClass; import javax.xml.bind.annotation.XmlAttribute; @@ -16,17 +17,21 @@ public class JApiClass { private List<JApiMethod> methods = new LinkedList<JApiMethod>(); private JApiChangeStatus changeStatus; private final Type type; + private Optional<AccessModifier> accessModifierOld; + private Optional<AccessModifier> accessModifierNew; public enum Type { ANNOTATION, INTERFACE, CLASS, ENUM } - public JApiClass(String fullyQualifiedName, Optional<CtClass> oldClass, Optional<CtClass> newClass, JApiChangeStatus changeStatus, Type type) { + public JApiClass(String fullyQualifiedName, Optional<CtClass> oldClass, Optional<CtClass> newClass, JApiChangeStatus changeStatus, Type type, Optional<AccessModifier> oldModifierLevel, Optional<AccessModifier> newModifierLevel) { this.changeStatus = changeStatus; this.fullyQualifiedName = fullyQualifiedName; this.newClass = newClass; this.oldClass = oldClass; this.type = type; + this.accessModifierOld = oldModifierLevel; + this.accessModifierNew = newModifierLevel; } public void addMethod(JApiMethod jApiMethod) { @@ -71,6 +76,22 @@ public class JApiClass { return type; } + @XmlAttribute(name = "accessModifierNew") + public String getAccessModifierNew() { + if(this.accessModifierNew.isPresent()) { + return this.accessModifierNew.get().toString(); + } + return "n.a."; + } + + @XmlAttribute(name = "accessModifierOld") + public String getAccessModifierOld() { + if(this.accessModifierOld.isPresent()) { + return this.accessModifierOld.get().toString(); + } + return "n.a."; + } + @Override public String toString() { return "JApiClass{" + diff --git a/japicmp/src/main/java/japicmp/model/JApiMethod.java b/japicmp/src/main/java/japicmp/model/JApiMethod.java index ab04e1f..b23c44a 100644 --- a/japicmp/src/main/java/japicmp/model/JApiMethod.java +++ b/japicmp/src/main/java/japicmp/model/JApiMethod.java @@ -1,6 +1,7 @@ package japicmp.model; import com.google.common.base.Optional; +import japicmp.cmp.AccessModifier; import javassist.CtMethod; import javax.xml.bind.annotation.XmlAttribute; @@ -15,14 +16,18 @@ public class JApiMethod { private final Optional<CtMethod> oldMethod; private final Optional<CtMethod> newMethod; private final String returnType; + private Optional<AccessModifier> accessModifierOld; + private Optional<AccessModifier> accessModifierNew; private final List<JApiParameter> parameters = new LinkedList<JApiParameter>(); - public JApiMethod(String name, JApiChangeStatus changeStatus, Optional<CtMethod> oldClass, Optional<CtMethod> newClass, String returnType) { + public JApiMethod(String name, JApiChangeStatus changeStatus, Optional<CtMethod> oldClass, Optional<CtMethod> newClass, String returnType, Optional<AccessModifier> oldModifierLevel, Optional<AccessModifier> newModifierLevel) { this.name = name; this.changeStatus = changeStatus; this.oldMethod = oldClass; this.newMethod = newClass; this.returnType = returnType; + this.accessModifierOld = oldModifierLevel; + this.accessModifierNew = newModifierLevel; } @XmlAttribute @@ -58,4 +63,20 @@ public class JApiMethod { public void addParameter(JApiParameter jApiParameter) { parameters.add(jApiParameter); } + + @XmlAttribute(name = "accessModifierNew") + public String getAccessModifierNew() { + if(this.accessModifierNew.isPresent()) { + return this.accessModifierNew.get().toString(); + } + return "n.a."; + } + + @XmlAttribute(name = "accessModifierOld") + public String getAccessModifierOld() { + if(this.accessModifierOld.isPresent()) { + return this.accessModifierOld.get().toString(); + } + return "n.a."; + } } diff --git a/japicmp/src/main/java/japicmp/output/stdout/StdoutOutputGenerator.java b/japicmp/src/main/java/japicmp/output/stdout/StdoutOutputGenerator.java index e379095..b981804 100644 --- a/japicmp/src/main/java/japicmp/output/stdout/StdoutOutputGenerator.java +++ b/japicmp/src/main/java/japicmp/output/stdout/StdoutOutputGenerator.java @@ -57,7 +57,7 @@ public class StdoutOutputGenerator { int paramCount = 0; for (JApiParameter jApiParameter : jApiMethod.getParameters()) { if (paramCount > 0) { - sb.append(","); + sb.append(", "); } sb.append(jApiParameter.getType()); paramCount++; diff --git a/japicmp/src/main/java/japicmp/util/ModifierHelper.java b/japicmp/src/main/java/japicmp/util/ModifierHelper.java new file mode 100644 index 0000000..416888e --- /dev/null +++ b/japicmp/src/main/java/japicmp/util/ModifierHelper.java @@ -0,0 +1,29 @@ +package japicmp.util; + +import japicmp.cmp.AccessModifier; + +import java.lang.reflect.Modifier; + +public class ModifierHelper { + + private ModifierHelper() { + + } + + public static boolean matchesModifierLevel(int modifierOfElement, AccessModifier modifierLevel) { + AccessModifier modifierLevelOfElement = translateToModifierLevel(modifierOfElement); + return (modifierLevelOfElement.getLevel() >= modifierLevel.getLevel()); + } + + public static AccessModifier translateToModifierLevel(int modifier) { + if(Modifier.isPublic(modifier)) { + return AccessModifier.PUBLIC; + } else if(Modifier.isProtected(modifier)) { + return AccessModifier.PROTECTED; + } else if(Modifier.isPrivate(modifier)) { + return AccessModifier.PRIVATE; + } else { + return AccessModifier.PACKAGE; + } + } +} diff --git a/japicmp/src/test/java/japicmp/util/ModifierHelperTest.java b/japicmp/src/test/java/japicmp/util/ModifierHelperTest.java new file mode 100644 index 0000000..4f0b95f --- /dev/null +++ b/japicmp/src/test/java/japicmp/util/ModifierHelperTest.java @@ -0,0 +1,41 @@ +package japicmp.util; + +import japicmp.cmp.AccessModifier; +import javassist.Modifier; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +public class ModifierHelperTest { + + @Test + public void publicToPublic() { + assertThat(ModifierHelper.matchesModifierLevel(Modifier.setPublic(0), AccessModifier.PUBLIC), is(true)); + } + + @Test + public void publicToProtected() { + assertThat(ModifierHelper.matchesModifierLevel(Modifier.setPublic(0), AccessModifier.PROTECTED), is(true)); + } + + @Test + public void publicToPrivate() { + assertThat(ModifierHelper.matchesModifierLevel(Modifier.setPublic(0), AccessModifier.PRIVATE), is(true)); + } + + @Test + public void privateToPublic() { + assertThat(ModifierHelper.matchesModifierLevel(Modifier.setPrivate(0), AccessModifier.PUBLIC), is(false)); + } + + @Test + public void privateToProtected() { + assertThat(ModifierHelper.matchesModifierLevel(Modifier.setPrivate(0), AccessModifier.PROTECTED), is(false)); + } + + @Test + public void privateToPrivate() { + assertThat(ModifierHelper.matchesModifierLevel(Modifier.setPrivate(0), AccessModifier.PRIVATE), is(true)); + } +}
