Skip to content

Commit

Permalink
#110: started tracking of exceptions on methods/constructors
Browse files Browse the repository at this point in the history
  • Loading branch information
siom79 committed Feb 23, 2016
1 parent 0bf469a commit 690a92a
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 4 deletions.
47 changes: 43 additions & 4 deletions japicmp/src/main/java/japicmp/model/JApiBehavior.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,13 @@
import javassist.CtMethod;
import javassist.Modifier;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ExceptionsAttribute;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlTransient;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.*;

public class JApiBehavior implements JApiHasModifiers, JApiHasChangeStatus, JApiHasAccessModifier, JApiHasStaticModifier,
JApiHasFinalModifier, JApiHasAbstractModifier, JApiCompatibility, JApiHasAnnotations, JApiHasBridgeModifier,
Expand All @@ -34,6 +32,7 @@ public class JApiBehavior implements JApiHasModifiers, JApiHasChangeStatus, JApi
private final JApiModifier<BridgeModifier> bridgeModifier;
private final JApiModifier<SyntheticModifier> syntheticModifier;
private final JApiAttribute<SyntheticAttribute> syntheticAttribute;
private final List<JApiException> exceptions;
protected JApiChangeStatus changeStatus;
private final Optional<Integer> oldLineNumber;
private final Optional<Integer> newLineNumber;
Expand All @@ -49,11 +48,45 @@ public JApiBehavior(String name, Optional<? extends CtBehavior> oldBehavior, Opt
this.bridgeModifier = extractBridgeModifier(oldBehavior, newBehavior);
this.syntheticModifier = extractSyntheticModifier(oldBehavior, newBehavior);
this.syntheticAttribute = extractSyntheticAttribute(oldBehavior, newBehavior);
this.exceptions = computeExceptionChanges(oldBehavior, newBehavior);
this.changeStatus = evaluateChangeStatus(changeStatus);
this.oldLineNumber = getLineNumber(oldBehavior);
this.newLineNumber = getLineNumber(newBehavior);
}

private List<JApiException> computeExceptionChanges(Optional<? extends CtBehavior> oldMethodOptional, Optional<? extends CtBehavior> newMethodOptional) {
List<JApiException> exceptionList = new ArrayList<>();
if (oldMethodOptional.isPresent() && newMethodOptional.isPresent()) {
List<String> oldExceptions = extractExceptions(oldMethodOptional);
List<String> newExceptions = extractExceptions(newMethodOptional);
for (String oldException : oldExceptions) {
if (newExceptions.contains(oldException)) {
exceptionList.add(new JApiException(oldException, JApiChangeStatus.UNCHANGED));
newExceptions.remove(oldException);
} else {
exceptionList.add(new JApiException(oldException, JApiChangeStatus.REMOVED));
}
}
for (String newException : newExceptions) {
exceptionList.add(new JApiException(newException, JApiChangeStatus.NEW));
}
}
return exceptionList;
}

private List<String> extractExceptions(Optional<? extends CtBehavior> methodOptional) {
ExceptionsAttribute exceptionsAttribute = methodOptional.get().getMethodInfo().getExceptionsAttribute();
String[] exceptions;
if (exceptionsAttribute != null) {
exceptions = exceptionsAttribute.getExceptions();
} else {
exceptions = new String[0];
}
List<String> list = new ArrayList<>(exceptions.length);
Collections.addAll(list, exceptions);
return list;
}

private Optional<Integer> getLineNumber(Optional<? extends CtBehavior> methodOptional) {
Optional<Integer> lineNumberOptional = Optional.absent();
if (methodOptional.isPresent()) {
Expand Down Expand Up @@ -387,4 +420,10 @@ public String getOldLineNumberAsString() {
public String getNewLineNumberAsString() {
return OptionalHelper.optionalToString(this.newLineNumber);
}

@XmlElementWrapper(name = "exceptions")
@XmlElement(name = "exception")
public List<JApiException> getExceptions() {
return exceptions;
}
}
23 changes: 23 additions & 0 deletions japicmp/src/main/java/japicmp/model/JApiException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package japicmp.model;

import javax.xml.bind.annotation.XmlAttribute;

public class JApiException implements JApiHasChangeStatus {
private final String name;
private final JApiChangeStatus changeStatus;

public JApiException(String name, JApiChangeStatus changeStatus) {
this.name = name;
this.changeStatus = changeStatus;
}

@XmlAttribute(name = "name")
public String getName() {
return name;
}

@XmlAttribute(name = "changeStatus")
public JApiChangeStatus getChangeStatus() {
return changeStatus;
}
}
5 changes: 5 additions & 0 deletions japicmp/src/main/java/japicmp/model/JApiMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@
import japicmp.cmp.JarArchiveComparatorOptions;
import japicmp.util.MethodDescriptorParser;
import javassist.CtMethod;
import javassist.bytecode.ExceptionsAttribute;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlTransient;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class JApiMethod extends JApiBehavior {
private final Optional<CtMethod> oldMethod;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ private void processConstructors(StringBuilder sb, JApiClass jApiClass) {
for (JApiConstructor jApiConstructor : constructors) {
appendMethod(sb, signs(jApiConstructor), jApiConstructor, "CONSTRUCTOR:");
processAnnotations(sb, jApiConstructor, 2);
processExceptions(sb, jApiConstructor, 2);
}
}

Expand All @@ -84,9 +85,20 @@ private void processMethods(StringBuilder sb, JApiClass jApiClass) {
for (JApiMethod jApiMethod : methods) {
appendMethod(sb, signs(jApiMethod), jApiMethod, "METHOD:");
processAnnotations(sb, jApiMethod, 2);
processExceptions(sb, jApiMethod, 2);
}
}

private void processExceptions(StringBuilder sb, JApiBehavior jApiBehavior, int indent) {
for (JApiException exception : jApiBehavior.getExceptions()) {
appendException(sb, signs(exception), exception, indent);
}
}

private void appendException(StringBuilder sb, String signs, JApiException jApiException, int indent) {
sb.append(tabs(indent)).append(signs).append(" ").append(jApiException.getChangeStatus()).append(" EXCEPTION: ").append(jApiException.getName()).append("\n");
}

private void processClass(StringBuilder sb, JApiClass jApiClass) {
appendClass(sb, signs(jApiClass), jApiClass);
}
Expand Down
26 changes: 26 additions & 0 deletions japicmp/src/main/resources/html.xslt
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@
<td>Modifier</td>
<td>Type</td>
<td>Method</td>
<td>Exceptions</td>
<td>Line Number</td>
</tr>
</thead>
Expand Down Expand Up @@ -476,6 +477,31 @@
<xsl:value-of select="@name"/>(<xsl:apply-templates select="parameters"/>)
<xsl:call-template name="annotations"/>
</td>
<td>
<xsl:if test="count(exceptions/exception) > 0">
<table>
<thead>
<tr>
<td>Status:</td>
<td>Name:</td>
</tr>
</thead>
<tbody>
<xsl:for-each select="exceptions/exception">
<tr>
<td>
<xsl:call-template name="outputChangeStatus"/>
</td>
<td>
<xsl:value-of select="@name"/>
</td>
</tr>
</xsl:for-each>
</tbody>
</table>
</xsl:if>
<xsl:if test="count(exceptions/exception) = 0">n.a.</xsl:if>
</td>
<td>
<table>
<thead>
Expand Down
93 changes: 93 additions & 0 deletions japicmp/src/test/java/japicmp/cmp/ExceptionsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package japicmp.cmp;

import japicmp.model.JApiChangeStatus;
import japicmp.model.JApiClass;
import japicmp.model.JApiMethod;
import japicmp.util.CtClassBuilder;
import japicmp.util.CtMethodBuilder;
import javassist.ClassPool;
import javassist.CtClass;
import org.junit.Test;

import java.util.Collections;
import java.util.List;

import static japicmp.util.Helper.getJApiClass;
import static japicmp.util.Helper.getJApiMethod;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;

public class ExceptionsTest {

@Test
public void testMethodThrowsUnchangedExceptions() throws Exception {
JarArchiveComparatorOptions options = new JarArchiveComparatorOptions();
List<JApiClass> jApiClasses = ClassesHelper.compareClasses(options, new ClassesHelper.ClassesGenerator() {
@Override
public List<CtClass> createOldClasses(ClassPool classPool) throws Exception {
CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool);
CtMethodBuilder.create().publicAccess().name("method").exceptions(new CtClass[] {classPool.get("java.lang.Exception")}).addToClass(ctClass);
return Collections.singletonList(ctClass);
}

@Override
public List<CtClass> createNewClasses(ClassPool classPool) throws Exception {
CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool);
CtMethodBuilder.create().publicAccess().name("method").exceptions(new CtClass[] {classPool.get("java.lang.Exception")}).addToClass(ctClass);
return Collections.singletonList(ctClass);
}
});
JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.Test");
JApiMethod method = getJApiMethod(jApiClass.getMethods(), "method");
assertThat(method.getExceptions().size(), is(1));
assertThat(method.getExceptions().get(0).getChangeStatus(), is(JApiChangeStatus.UNCHANGED));
}

@Test
public void testMethodThrowsNewExceptions() throws Exception {
JarArchiveComparatorOptions options = new JarArchiveComparatorOptions();
List<JApiClass> jApiClasses = ClassesHelper.compareClasses(options, new ClassesHelper.ClassesGenerator() {
@Override
public List<CtClass> createOldClasses(ClassPool classPool) throws Exception {
CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool);
CtMethodBuilder.create().publicAccess().name("method").addToClass(ctClass);
return Collections.singletonList(ctClass);
}

@Override
public List<CtClass> createNewClasses(ClassPool classPool) throws Exception {
CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool);
CtMethodBuilder.create().publicAccess().name("method").exceptions(new CtClass[] {classPool.get("java.lang.Exception")}).addToClass(ctClass);
return Collections.singletonList(ctClass);
}
});
JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.Test");
JApiMethod method = getJApiMethod(jApiClass.getMethods(), "method");
assertThat(method.getExceptions().size(), is(1));
assertThat(method.getExceptions().get(0).getChangeStatus(), is(JApiChangeStatus.NEW));
}

@Test
public void testMethodThrowsRemovedExceptions() throws Exception {
JarArchiveComparatorOptions options = new JarArchiveComparatorOptions();
List<JApiClass> jApiClasses = ClassesHelper.compareClasses(options, new ClassesHelper.ClassesGenerator() {
@Override
public List<CtClass> createOldClasses(ClassPool classPool) throws Exception {
CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool);
CtMethodBuilder.create().publicAccess().name("method").exceptions(new CtClass[] {classPool.get("java.lang.Exception")}).addToClass(ctClass);
return Collections.singletonList(ctClass);
}

@Override
public List<CtClass> createNewClasses(ClassPool classPool) throws Exception {
CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool);
CtMethodBuilder.create().publicAccess().name("method").addToClass(ctClass);
return Collections.singletonList(ctClass);
}
});
JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.Test");
JApiMethod method = getJApiMethod(jApiClass.getMethods(), "method");
assertThat(method.getExceptions().size(), is(1));
assertThat(method.getExceptions().get(0).getChangeStatus(), is(JApiChangeStatus.REMOVED));
}
}

0 comments on commit 690a92a

Please sign in to comment.