analyze(URL url) throws IOException {
+ ClassMembersVisitorCounter.resetClassCounters();
+ DependencyClassFileVisitor visitor = new DependencyClassFileVisitor();
+ ClassFileVisitorUtils.accept(url, visitor);
+ return visitor.getDependencies();
+ }
}
diff --git a/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/ConstantPoolParser.java b/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/ConstantPoolParser.java
index 55ab8040..ce505784 100644
--- a/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/ConstantPoolParser.java
+++ b/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/ConstantPoolParser.java
@@ -19,8 +19,6 @@
package se.kth.depclean.core.analysis.asm;
-import org.jetbrains.annotations.NotNull;
-
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Collections;
@@ -28,157 +26,157 @@
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import org.jetbrains.annotations.NotNull;
/**
- * A small parser to read the constant pool directly, in case it contains references
- * ASM does not support.
- *
- * Adapted from http://stackoverflow.com/a/32278587/23691
- *
- * Constant pool types:
+ * A small parser to read the constant pool directly, in case it contains references ASM does not support.
+ *
+ *
Adapted from http://stackoverflow.com/a/32278587/23691
+ *
+ *
Constant pool types:
*
* @see JVM 9 Sepc
* @see JVM 10 Sepc
*/
public class ConstantPoolParser {
- public static final int HEAD = 0xcafebabe;
+ public static final int HEAD = 0xcafebabe;
- // Constant pool types
- public static final byte CONSTANT_UTF8 = 1;
+ // Constant pool types
+ public static final byte CONSTANT_UTF8 = 1;
- public static final byte CONSTANT_INTEGER = 3;
+ public static final byte CONSTANT_INTEGER = 3;
- public static final byte CONSTANT_FLOAT = 4;
+ public static final byte CONSTANT_FLOAT = 4;
- public static final byte CONSTANT_LONG = 5;
+ public static final byte CONSTANT_LONG = 5;
- public static final byte CONSTANT_DOUBLE = 6;
+ public static final byte CONSTANT_DOUBLE = 6;
- public static final byte CONSTANT_CLASS = 7;
+ public static final byte CONSTANT_CLASS = 7;
- public static final byte CONSTANT_STRING = 8;
+ public static final byte CONSTANT_STRING = 8;
- public static final byte CONSTANT_FIELDREF = 9;
+ public static final byte CONSTANT_FIELDREF = 9;
- public static final byte CONSTANT_METHODREF = 10;
+ public static final byte CONSTANT_METHODREF = 10;
- public static final byte CONSTANT_INTERFACEMETHODREF = 11;
+ public static final byte CONSTANT_INTERFACEMETHODREF = 11;
- public static final byte CONSTANT_NAME_AND_TYPE = 12;
+ public static final byte CONSTANT_NAME_AND_TYPE = 12;
- public static final byte CONSTANT_METHODHANDLE = 15;
+ public static final byte CONSTANT_METHODHANDLE = 15;
- public static final byte CONSTANT_METHOD_TYPE = 16;
+ public static final byte CONSTANT_METHOD_TYPE = 16;
- public static final byte CONSTANT_INVOKE_DYNAMIC = 18;
+ public static final byte CONSTANT_INVOKE_DYNAMIC = 18;
- public static final byte CONSTANT_MODULE = 19;
+ public static final byte CONSTANT_MODULE = 19;
- public static final byte CONSTANT_PACKAGE = 20;
+ public static final byte CONSTANT_PACKAGE = 20;
- private static final int OXF0 = 0xf0;
+ private static final int OXF0 = 0xf0;
- private static final int OXE0 = 0xe0;
+ private static final int OXE0 = 0xe0;
- private static final int OX3F = 0x3F;
+ private static final int OX3F = 0x3F;
- private ConstantPoolParser() {
- throw new IllegalStateException("Utility class");
- }
+ private ConstantPoolParser() {
+ throw new IllegalStateException("Utility class");
+ }
- static Set getConstantPoolClassReferences(byte[] b) {
- return parseConstantPoolClassReferences(ByteBuffer.wrap(b));
- }
+ static Set getConstantPoolClassReferences(byte[] b) {
+ return parseConstantPoolClassReferences(ByteBuffer.wrap(b));
+ }
- static Set parseConstantPoolClassReferences(ByteBuffer buf) {
- if (buf.order(ByteOrder.BIG_ENDIAN)
- .getInt() != HEAD) {
- return Collections.emptySet();
- }
- buf.getChar();
- buf.getChar(); // minor + ver
- Set classes = new HashSet<>();
- Map stringConstants = new HashMap<>();
- for (int ix = 1, num = buf.getChar(); ix < num; ix++) {
- byte tag = buf.get();
- switch (tag) {
- case CONSTANT_UTF8:
- stringConstants.put(ix, decodeString(buf));
- continue;
- case CONSTANT_CLASS:
- case CONSTANT_STRING:
- case CONSTANT_METHOD_TYPE:
- classes.add((int) buf.getChar());
- break;
- case CONSTANT_FIELDREF:
- case CONSTANT_METHODREF:
- case CONSTANT_INTERFACEMETHODREF:
- case CONSTANT_NAME_AND_TYPE:
- buf.getChar();
- buf.getChar();
- break;
- case CONSTANT_INTEGER:
- buf.getInt();
- break;
- case CONSTANT_FLOAT:
- buf.getFloat();
- break;
- case CONSTANT_DOUBLE:
- buf.getDouble();
- ix++;
- break;
- case CONSTANT_LONG:
- buf.getLong();
- ix++;
- break;
- case CONSTANT_METHODHANDLE:
- buf.get();
- buf.getChar();
- break;
- case CONSTANT_INVOKE_DYNAMIC:
- buf.getChar();
- buf.getChar();
- break;
- case CONSTANT_MODULE:
- buf.getChar();
- break;
- case CONSTANT_PACKAGE:
- buf.getChar();
- break;
- default:
- throw new RuntimeException("Unknown constant pool type '" + tag + "'");
- }
- }
- Set result = new HashSet<>();
- for (Integer aClass : classes) {
- result.add(stringConstants.get(aClass));
- }
- return result;
+ static Set parseConstantPoolClassReferences(ByteBuffer buf) {
+ if (buf.order(ByteOrder.BIG_ENDIAN)
+ .getInt() != HEAD) {
+ return Collections.emptySet();
}
-
- @NotNull
- private static String decodeString(ByteBuffer buf) {
- int size = buf.getChar();
- // Explicit cast for compatibility with covariant return type on JDK 9's ByteBuffer
- int oldLimit = buf.limit();
- buf.limit(buf.position() + size);
- StringBuilder sb = new StringBuilder(size + (size >> 1) + 16);
- while (buf.hasRemaining()) {
- byte b = buf.get();
- if (b > 0) {
- sb.append((char) b);
- } else {
- int b2 = buf.get();
- if ((b & OXF0) != OXE0) {
- sb.append((char) ((b & 0x1F) << 6 | b2 & OX3F));
- } else {
- int b3 = buf.get();
- sb.append((char) ((b & 0x0F) << 12 | (b2 & OX3F) << 6 | b3 & OX3F));
- }
- }
+ buf.getChar();
+ buf.getChar(); // minor + ver
+ Set classes = new HashSet<>();
+ Map stringConstants = new HashMap<>();
+ for (int ix = 1, num = buf.getChar(); ix < num; ix++) {
+ byte tag = buf.get();
+ switch (tag) {
+ case CONSTANT_UTF8:
+ stringConstants.put(ix, decodeString(buf));
+ continue;
+ case CONSTANT_CLASS:
+ case CONSTANT_STRING:
+ case CONSTANT_METHOD_TYPE:
+ classes.add((int) buf.getChar());
+ break;
+ case CONSTANT_FIELDREF:
+ case CONSTANT_METHODREF:
+ case CONSTANT_INTERFACEMETHODREF:
+ case CONSTANT_NAME_AND_TYPE:
+ buf.getChar();
+ buf.getChar();
+ break;
+ case CONSTANT_INTEGER:
+ buf.getInt();
+ break;
+ case CONSTANT_FLOAT:
+ buf.getFloat();
+ break;
+ case CONSTANT_DOUBLE:
+ buf.getDouble();
+ ix++;
+ break;
+ case CONSTANT_LONG:
+ buf.getLong();
+ ix++;
+ break;
+ case CONSTANT_METHODHANDLE:
+ buf.get();
+ buf.getChar();
+ break;
+ case CONSTANT_INVOKE_DYNAMIC:
+ buf.getChar();
+ buf.getChar();
+ break;
+ case CONSTANT_MODULE:
+ buf.getChar();
+ break;
+ case CONSTANT_PACKAGE:
+ buf.getChar();
+ break;
+ default:
+ throw new RuntimeException("Unknown constant pool type '" + tag + "'");
+ }
+ }
+ Set result = new HashSet<>();
+ for (Integer clazz : classes) {
+ result.add(stringConstants.get(clazz));
+ }
+ return result;
+ }
+
+ @NotNull
+ private static String decodeString(ByteBuffer buf) {
+ int size = buf.getChar();
+ // Explicit cast for compatibility with covariant return type on JDK 9's ByteBuffer
+ int oldLimit = buf.limit();
+ buf.limit(buf.position() + size);
+ StringBuilder sb = new StringBuilder(size + (size >> 1) + 16);
+ while (buf.hasRemaining()) {
+ byte b = buf.get();
+ if (b > 0) {
+ sb.append((char) b);
+ } else {
+ int b2 = buf.get();
+ if ((b & OXF0) != OXE0) {
+ sb.append((char) ((b & 0x1F) << 6 | b2 & OX3F));
+ } else {
+ int b3 = buf.get();
+ sb.append((char) ((b & 0x0F) << 12 | (b2 & OX3F) << 6 | b3 & OX3F));
}
- buf.limit(oldLimit);
- return sb.toString();
+ }
}
+ buf.limit(oldLimit);
+ return sb.toString();
+ }
}
diff --git a/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DefaultAnnotationVisitor.java b/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DefaultAnnotationVisitor.java
index 19ff60c5..ea818abf 100644
--- a/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DefaultAnnotationVisitor.java
+++ b/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DefaultAnnotationVisitor.java
@@ -24,42 +24,42 @@
import org.objectweb.asm.Type;
/**
- * Computes the set of classes referenced by visited code.
- * Inspired by org.objectweb.asm.depend.DependencyVisitor
in the ASM dependencies example.
+ * Computes the set of classes referenced by visited code. Inspired by
+ * org.objectweb.asm.depend.DependencyVisitor
in the ASM dependencies example.
*/
public class DefaultAnnotationVisitor extends AnnotationVisitor {
- private final ResultCollector resultCollector;
+ private final ResultCollector resultCollector;
- public DefaultAnnotationVisitor(ResultCollector resultCollector) {
- super(Opcodes.ASM7);
- this.resultCollector = resultCollector;
- }
+ public DefaultAnnotationVisitor(ResultCollector resultCollector) {
+ super(Opcodes.ASM7);
+ this.resultCollector = resultCollector;
+ }
- @Override
- public void visit(final String name, final Object value) {
- if (value instanceof Type) {
- resultCollector.addType((Type) value);
- }
+ @Override
+ public void visit(final String name, final Object value) {
+ if (value instanceof Type) {
+ resultCollector.addType((Type) value);
}
+ }
- @Override
- public void visitEnum(final String name, final String desc, final String value) {
- resultCollector.addDesc(desc);
- }
+ @Override
+ public void visitEnum(final String name, final String desc, final String value) {
+ resultCollector.addDesc(desc);
+ }
- @Override
- public AnnotationVisitor visitAnnotation(final String name, final String desc) {
- resultCollector.addDesc(desc);
+ @Override
+ public AnnotationVisitor visitAnnotation(final String name, final String desc) {
+ resultCollector.addDesc(desc);
- return this;
- }
+ return this;
+ }
- /*
- * @see org.objectweb.asm.AnnotationVisitor#visitArray(java.lang.String)
- */
- @Override
- public AnnotationVisitor visitArray(final String name) {
- return this;
- }
+ /*
+ * @see org.objectweb.asm.AnnotationVisitor#visitArray(java.lang.String)
+ */
+ @Override
+ public AnnotationVisitor visitArray(final String name) {
+ return this;
+ }
}
diff --git a/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DefaultClassVisitor.java b/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DefaultClassVisitor.java
index 14f17751..b4c7f158 100644
--- a/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DefaultClassVisitor.java
+++ b/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DefaultClassVisitor.java
@@ -30,102 +30,102 @@
import se.kth.depclean.core.analysis.graph.ClassMembersVisitorCounter;
/**
- * Computes the set of classes referenced by visited code.
- * Inspired by org.objectweb.asm.depend.DependencyVisitor
in the ASM dependencies example.
+ * Computes the set of classes referenced by visited code. Inspired by
+ * org.objectweb.asm.depend.DependencyVisitor
in the ASM dependencies example.
*/
public class DefaultClassVisitor extends ClassVisitor {
- private final ResultCollector resultCollector;
- private final SignatureVisitor signatureVisitor;
- private final AnnotationVisitor annotationVisitor;
- private final FieldVisitor fieldVisitor;
- private final MethodVisitor methodVisitor;
+ private final ResultCollector resultCollector;
+ private final SignatureVisitor signatureVisitor;
+ private final AnnotationVisitor annotationVisitor;
+ private final FieldVisitor fieldVisitor;
+ private final MethodVisitor methodVisitor;
- public DefaultClassVisitor(SignatureVisitor signatureVisitor,
- AnnotationVisitor annotationVisitor,
- FieldVisitor fieldVisitor,
- MethodVisitor methodVisitor,
- ResultCollector resultCollector) {
- super(Opcodes.ASM7);
- this.signatureVisitor = signatureVisitor;
- this.annotationVisitor = annotationVisitor;
- this.fieldVisitor = fieldVisitor;
- this.methodVisitor = methodVisitor;
- this.resultCollector = resultCollector;
- }
+ public DefaultClassVisitor(SignatureVisitor signatureVisitor,
+ AnnotationVisitor annotationVisitor,
+ FieldVisitor fieldVisitor,
+ MethodVisitor methodVisitor,
+ ResultCollector resultCollector) {
+ super(Opcodes.ASM7);
+ this.signatureVisitor = signatureVisitor;
+ this.annotationVisitor = annotationVisitor;
+ this.fieldVisitor = fieldVisitor;
+ this.methodVisitor = methodVisitor;
+ this.resultCollector = resultCollector;
+ }
- @Override
- public void visit(final int version, final int access, final String name, final String signature,
- final String superName, final String[] interfaces) {
- // System.out.println("Visiting class: " + name);
- ClassMembersVisitorCounter.addVisitedClass();
- if (signature == null) {
- resultCollector.addName(superName);
- resultCollector.addNames(interfaces);
- } else {
- addSignature(signature);
- }
- }
+ @Override
+ public void visit(final int version, final int access, final String name, final String signature,
+ final String superName, final String[] interfaces) {
+ // System.out.println("Visiting class: " + name);
+ ClassMembersVisitorCounter.addVisitedClass();
+ if (signature == null) {
+ resultCollector.addName(superName);
+ resultCollector.addNames(interfaces);
+ } else {
+ addSignature(signature);
+ }
+ }
- @Override
- public void visitNestHost(final String nestHost) {
- resultCollector.addName(nestHost);
- }
+ @Override
+ public void visitNestHost(final String nestHost) {
+ resultCollector.addName(nestHost);
+ }
- @Override
- public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
- // System.out.println("\t" + "visiting annotation: " + desc);
- ClassMembersVisitorCounter.addVisitedAnnotation();
- resultCollector.addDesc(desc);
- return annotationVisitor;
- }
+ @Override
+ public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
+ // System.out.println("\t" + "visiting annotation: " + desc);
+ ClassMembersVisitorCounter.addVisitedAnnotation();
+ resultCollector.addDesc(desc);
+ return annotationVisitor;
+ }
- @Override
- public void visitNestMember(final String nestMember) {
- resultCollector.addName(nestMember);
- }
+ @Override
+ public void visitNestMember(final String nestMember) {
+ resultCollector.addName(nestMember);
+ }
- @Override
- public FieldVisitor visitField(final int access, final String name, final String desc, final String signature,
- final Object value) {
- // System.out.println("\t" + "visiting field: " + name);
- ClassMembersVisitorCounter.addVisitedField();
- if (signature == null) {
- resultCollector.addDesc(desc);
- } else {
- addTypeSignature(signature);
- }
- if (value instanceof Type) {
- resultCollector.addType((Type) value);
- }
- return fieldVisitor;
- }
+ @Override
+ public FieldVisitor visitField(final int access, final String name, final String desc, final String signature,
+ final Object value) {
+ // System.out.println("\t" + "visiting field: " + name);
+ ClassMembersVisitorCounter.addVisitedField();
+ if (signature == null) {
+ resultCollector.addDesc(desc);
+ } else {
+ addTypeSignature(signature);
+ }
+ if (value instanceof Type) {
+ resultCollector.addType((Type) value);
+ }
+ return fieldVisitor;
+ }
- @Override
- public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature,
- final String[] exceptions) {
- // System.out.println("\t" + "visiting method: " + name);
- ClassMembersVisitorCounter.addVisitedMethod();
- if (signature == null) {
- resultCollector.addMethodDesc(desc);
- } else {
- addSignature(signature);
- }
- resultCollector.addNames(exceptions);
- return methodVisitor;
- }
+ @Override
+ public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature,
+ final String[] exceptions) {
+ // System.out.println("\t" + "visiting method: " + name);
+ ClassMembersVisitorCounter.addVisitedMethod();
+ if (signature == null) {
+ resultCollector.addMethodDesc(desc);
+ } else {
+ addSignature(signature);
+ }
+ resultCollector.addNames(exceptions);
+ return methodVisitor;
+ }
- // private methods --------------------------------------------------------
+ // private methods --------------------------------------------------------
- private void addTypeSignature(final String signature) {
- if (signature != null) {
- new SignatureReader(signature).acceptType(signatureVisitor);
- }
- }
+ private void addTypeSignature(final String signature) {
+ if (signature != null) {
+ new SignatureReader(signature).acceptType(signatureVisitor);
+ }
+ }
- private void addSignature(final String signature) {
- if (signature != null) {
- new SignatureReader(signature).accept(signatureVisitor);
- }
- }
+ private void addSignature(final String signature) {
+ if (signature != null) {
+ new SignatureReader(signature).accept(signatureVisitor);
+ }
+ }
}
diff --git a/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DefaultFieldVisitor.java b/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DefaultFieldVisitor.java
index 8fce37d8..62742e38 100644
--- a/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DefaultFieldVisitor.java
+++ b/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DefaultFieldVisitor.java
@@ -24,23 +24,23 @@
import org.objectweb.asm.Opcodes;
/**
- * Computes the set of classes referenced by visited code.
- * Inspired by org.objectweb.asm.depend.DependencyVisitor
in the ASM dependencies example.
+ * Computes the set of classes referenced by visited code. Inspired by
+ * org.objectweb.asm.depend.DependencyVisitor
in the ASM dependencies example.
*/
public class DefaultFieldVisitor extends FieldVisitor {
- private final AnnotationVisitor annotationVisitor;
- private final ResultCollector resultCollector;
+ private final AnnotationVisitor annotationVisitor;
+ private final ResultCollector resultCollector;
- public DefaultFieldVisitor(AnnotationVisitor annotationVisitor, ResultCollector resultCollector) {
- super(Opcodes.ASM7);
- this.annotationVisitor = annotationVisitor;
- this.resultCollector = resultCollector;
- }
+ public DefaultFieldVisitor(AnnotationVisitor annotationVisitor, ResultCollector resultCollector) {
+ super(Opcodes.ASM7);
+ this.annotationVisitor = annotationVisitor;
+ this.resultCollector = resultCollector;
+ }
- @Override
- public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
- resultCollector.addDesc(desc);
- return annotationVisitor;
- }
+ @Override
+ public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
+ resultCollector.addDesc(desc);
+ return annotationVisitor;
+ }
}
diff --git a/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DefaultMethodVisitor.java b/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DefaultMethodVisitor.java
index 84ecd30a..52801835 100644
--- a/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DefaultMethodVisitor.java
+++ b/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DefaultMethodVisitor.java
@@ -29,103 +29,103 @@
import org.objectweb.asm.signature.SignatureVisitor;
/**
- * Computes the set of classes referenced by visited code.
- * Inspired by org.objectweb.asm.depend.DependencyVisitor
in the ASM dependencies example.
+ * Computes the set of classes referenced by visited code. Inspired by
+ * org.objectweb.asm.depend.DependencyVisitor
in the ASM dependencies example.
*/
public class DefaultMethodVisitor extends MethodVisitor {
- private final AnnotationVisitor annotationVisitor;
- private final SignatureVisitor signatureVisitor;
- private final ResultCollector resultCollector;
-
- public DefaultMethodVisitor(AnnotationVisitor annotationVisitor, SignatureVisitor signatureVisitor,
- ResultCollector resultCollector) {
- super(Opcodes.ASM7);
- this.annotationVisitor = annotationVisitor;
- this.signatureVisitor = signatureVisitor;
- this.resultCollector = resultCollector;
- }
-
- @Override
- public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
- resultCollector.addDesc(desc);
- return annotationVisitor;
- }
-
- @Override
- public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
- resultCollector.addDesc(desc);
- return annotationVisitor;
- }
-
- @Override
- public AnnotationVisitor visitParameterAnnotation(final int parameter, final String desc, final boolean visible) {
- resultCollector.addDesc(desc);
- return annotationVisitor;
- }
-
- @Override
- public void visitTypeInsn(final int opcode, final String desc) {
- if (desc.charAt(0) == '[') {
- resultCollector.addDesc(desc);
- } else {
- resultCollector.addName(desc);
- }
- }
-
- @Override
- public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) {
- resultCollector.addName(owner);
- /*
- * NOTE: Merely accessing a field does not impose a direct dependency on its type. For example, the code line
- * java.lang.Object var = bean.field;
does not directly depend on the type of the field. A direct
- * dependency is only introduced when the code explicitly references the field's type by means of a variable
- * declaration or a type check/cast. Those cases are handled by other visitor callbacks.
- */
+ private final AnnotationVisitor annotationVisitor;
+ private final SignatureVisitor signatureVisitor;
+ private final ResultCollector resultCollector;
+
+ public DefaultMethodVisitor(AnnotationVisitor annotationVisitor, SignatureVisitor signatureVisitor,
+ ResultCollector resultCollector) {
+ super(Opcodes.ASM7);
+ this.annotationVisitor = annotationVisitor;
+ this.signatureVisitor = signatureVisitor;
+ this.resultCollector = resultCollector;
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
+ resultCollector.addDesc(desc);
+ return annotationVisitor;
+ }
+
+ @Override
+ public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
+ resultCollector.addDesc(desc);
+ return annotationVisitor;
+ }
+
+ @Override
+ public AnnotationVisitor visitParameterAnnotation(final int parameter, final String desc, final boolean visible) {
+ resultCollector.addDesc(desc);
+ return annotationVisitor;
+ }
+
+ @Override
+ public void visitTypeInsn(final int opcode, final String desc) {
+ if (desc.charAt(0) == '[') {
+ resultCollector.addDesc(desc);
+ } else {
+ resultCollector.addName(desc);
}
-
- @Override
- public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
- resultCollector.addName(owner);
- }
-
- @Override
- public void visitLdcInsn(final Object cst) {
- if (cst instanceof Type) {
- resultCollector.addType((Type) cst);
- }
+ }
+
+ @Override
+ public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) {
+ resultCollector.addName(owner);
+ /*
+ * NOTE: Merely accessing a field does not impose a direct dependency on its type. For example, the code line
+ * java.lang.Object var = bean.field;
does not directly depend on the type of the field. A direct
+ * dependency is only introduced when the code explicitly references the field's type by means of a variable
+ * declaration or a type check/cast. Those cases are handled by other visitor callbacks.
+ */
+ }
+
+ @Override
+ public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
+ resultCollector.addName(owner);
+ }
+
+ @Override
+ public void visitLdcInsn(final Object cst) {
+ if (cst instanceof Type) {
+ resultCollector.addType((Type) cst);
}
-
- @Override
- public void visitMultiANewArrayInsn(final String desc, final int dims) {
- resultCollector.addDesc(desc);
- }
-
- @Override
- public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) {
- resultCollector.addName(type);
+ }
+
+ @Override
+ public void visitMultiANewArrayInsn(final String desc, final int dims) {
+ resultCollector.addDesc(desc);
+ }
+
+ @Override
+ public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) {
+ resultCollector.addName(type);
+ }
+
+ @Override
+ public void visitLocalVariable(final String name, final String desc, final String signature, final Label start,
+ final Label end, final int index) {
+ if (signature == null) {
+ resultCollector.addDesc(desc);
+ } else {
+ addTypeSignature(signature);
}
-
- @Override
- public void visitLocalVariable(final String name, final String desc, final String signature, final Label start,
- final Label end, final int index) {
- if (signature == null) {
- resultCollector.addDesc(desc);
- } else {
- addTypeSignature(signature);
- }
- }
-
- @Override
- public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end,
- int[] index, String desc, boolean visible) {
- resultCollector.addDesc(desc);
- return annotationVisitor;
- }
-
- private void addTypeSignature(final String signature) {
- if (signature != null) {
- new SignatureReader(signature).acceptType(signatureVisitor);
- }
+ }
+
+ @Override
+ public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end,
+ int[] index, String desc, boolean visible) {
+ resultCollector.addDesc(desc);
+ return annotationVisitor;
+ }
+
+ private void addTypeSignature(final String signature) {
+ if (signature != null) {
+ new SignatureReader(signature).acceptType(signatureVisitor);
}
+ }
}
diff --git a/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DefaultSignatureVisitor.java b/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DefaultSignatureVisitor.java
index 3d188886..1a160f4c 100644
--- a/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DefaultSignatureVisitor.java
+++ b/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DefaultSignatureVisitor.java
@@ -23,25 +23,25 @@
import org.objectweb.asm.signature.SignatureVisitor;
/**
- * Computes the set of classes referenced by visited code.
- * Inspired by org.objectweb.asm.depend.DependencyVisitor
in the ASM dependencies example.
+ * Computes the set of classes referenced by visited code. Inspired by
+ * org.objectweb.asm.depend.DependencyVisitor
in the ASM dependencies example.
*/
public class DefaultSignatureVisitor extends SignatureVisitor {
- private final ResultCollector resultCollector;
+ private final ResultCollector resultCollector;
- public DefaultSignatureVisitor(ResultCollector resultCollector) {
- super(Opcodes.ASM7);
- this.resultCollector = resultCollector;
- }
+ public DefaultSignatureVisitor(ResultCollector resultCollector) {
+ super(Opcodes.ASM7);
+ this.resultCollector = resultCollector;
+ }
- @Override
- public void visitClassType(final String name) {
- resultCollector.addName(name);
- }
+ @Override
+ public void visitClassType(final String name) {
+ resultCollector.addName(name);
+ }
- @Override
- public void visitInnerClassType(final String name) {
- resultCollector.addName(name);
- }
+ @Override
+ public void visitInnerClassType(final String name) {
+ resultCollector.addName(name);
+ }
}
diff --git a/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DependencyClassFileVisitor.java b/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DependencyClassFileVisitor.java
index 6bad5f01..4a1c7676 100644
--- a/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DependencyClassFileVisitor.java
+++ b/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/DependencyClassFileVisitor.java
@@ -19,6 +19,9 @@
package se.kth.depclean.core.analysis.asm;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
@@ -28,10 +31,6 @@
import se.kth.depclean.core.analysis.ClassFileVisitor;
import se.kth.depclean.core.analysis.graph.DefaultCallGraph;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Set;
-
/**
* Computes the set of classes referenced by visited class files, using
* DependencyVisitor.
@@ -41,65 +40,67 @@
@Slf4j
public class DependencyClassFileVisitor implements ClassFileVisitor {
- private final ResultCollector resultCollector = new ResultCollector();
+ private final ResultCollector resultCollector = new ResultCollector();
- /*
- * @see org.apache.invoke.shared.dependency.analyzer.ClassFileVisitor#visitClass(java.lang.String,
- * java.io.InputStream)
- */
- public void visitClass(String className, InputStream in) {
- try {
- ClassReader reader = new ClassReader(in);
+ /*
+ * @see org.apache.invoke.shared.dependency.analyzer.ClassFileVisitor#visitClass(java.lang.String,
+ * java.io.InputStream)
+ */
+ public void visitClass(String className, InputStream in) {
+ try {
+ ClassReader reader = new ClassReader(in);
- final Set constantPoolClassRefs = ConstantPoolParser.getConstantPoolClassReferences(reader.b);
- for (String string : constantPoolClassRefs) {
- resultCollector.addName(string);
- }
+ final Set constantPoolClassRefs = ConstantPoolParser.getConstantPoolClassReferences(reader.b);
+ for (String string : constantPoolClassRefs) {
+ resultCollector.addName(string);
+ }
- /* visit class members */
- AnnotationVisitor annotationVisitor = new DefaultAnnotationVisitor(
- resultCollector
- );
- SignatureVisitor signatureVisitor = new DefaultSignatureVisitor(
- resultCollector
- );
- FieldVisitor fieldVisitor = new DefaultFieldVisitor(
- annotationVisitor,
- resultCollector
- );
- MethodVisitor methodVisitor = new DefaultMethodVisitor(
- annotationVisitor,
- signatureVisitor,
- resultCollector
- );
+ /* visit class members */
+ AnnotationVisitor annotationVisitor = new DefaultAnnotationVisitor(
+ resultCollector
+ );
+ SignatureVisitor signatureVisitor = new DefaultSignatureVisitor(
+ resultCollector
+ );
+ FieldVisitor fieldVisitor = new DefaultFieldVisitor(
+ annotationVisitor,
+ resultCollector
+ );
+ MethodVisitor methodVisitor = new DefaultMethodVisitor(
+ annotationVisitor,
+ signatureVisitor,
+ resultCollector
+ );
- DefaultClassVisitor defaultClassVisitor = new DefaultClassVisitor(
- signatureVisitor,
- annotationVisitor,
- fieldVisitor,
- methodVisitor,
- resultCollector
- );
+ DefaultClassVisitor defaultClassVisitor = new DefaultClassVisitor(
+ signatureVisitor,
+ annotationVisitor,
+ fieldVisitor,
+ methodVisitor,
+ resultCollector
+ );
- reader.accept(defaultClassVisitor, 0);
+ reader.accept(defaultClassVisitor, 0);
- // inset edge in the graph based on the bytecode analysis
- DefaultCallGraph.addEdge(className, resultCollector.getDependencies());
+ // inset edge in the graph based on the bytecode analysis
+ DefaultCallGraph.addEdge(className, resultCollector.getDependencies());
- } catch (IndexOutOfBoundsException | IOException e) {
- // some bug inside ASM causes an IOB exception. Log it and move on?
- // this happens when the class isn't valid.
- log.warn("Unable to process: " + className);
- }
- resultCollector.clearClasses();
- }
+ } catch (IndexOutOfBoundsException | IOException e) {
+ // some bug inside ASM causes an IOB exception. Log it and move on?
+ // this happens when the class isn't valid.
+ log.warn("Unable to process: " + className);
+ }
+ resultCollector.clearClasses();
+ }
- // public methods ---------------------------------------------------------
+ // public methods ---------------------------------------------------------
- /**
- * @return the set of classes referenced by visited class files
- */
- public Set getDependencies() {
- return resultCollector.getDependencies();
- }
+ /**
+ * Getter.
+ *
+ * @return the set of classes referenced by visited class files
+ */
+ public Set getDependencies() {
+ return resultCollector.getDependencies();
+ }
}
diff --git a/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/ResultCollector.java b/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/ResultCollector.java
index 10497b73..d806acdb 100644
--- a/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/ResultCollector.java
+++ b/depclean-core/src/main/java/se/kth/depclean/core/analysis/asm/ResultCollector.java
@@ -19,70 +19,69 @@
package se.kth.depclean.core.analysis.asm;
-import org.objectweb.asm.Type;
-
import java.util.HashSet;
import java.util.Set;
+import org.objectweb.asm.Type;
public class ResultCollector {
- private final Set classes = new HashSet<>();
+ private final Set classes = new HashSet<>();
- public Set getDependencies() {
- return classes;
- }
+ public Set getDependencies() {
+ return classes;
+ }
- public void clearClasses() {
- classes.clear();
- }
+ public void clearClasses() {
+ classes.clear();
+ }
- void addDesc(final String desc) {
- addType(Type.getType(desc));
- }
+ void addDesc(final String desc) {
+ addType(Type.getType(desc));
+ }
- void addType(final Type t) {
- switch (t.getSort()) {
- case Type.ARRAY:
- addType(t.getElementType());
- break;
- case Type.OBJECT:
- addName(t.getClassName().replace('.', '/'));
- break;
- default:
- }
+ void addType(final Type t) {
+ switch (t.getSort()) {
+ case Type.ARRAY:
+ addType(t.getElementType());
+ break;
+ case Type.OBJECT:
+ addName(t.getClassName().replace('.', '/'));
+ break;
+ default:
}
+ }
- public void add(String name) {
- classes.add(name);
- }
+ public void add(String name) {
+ classes.add(name);
+ }
- void addNames(final String[] names) {
- if (names == null) {
- return;
- }
- for (String name : names) {
- addName(name);
- }
+ void addNames(final String[] names) {
+ if (names == null) {
+ return;
}
+ for (String name : names) {
+ addName(name);
+ }
+ }
- public void addName(String name) {
- if (name == null) {
- return;
- }
- // decode arrays
- if (name.startsWith("[L") && name.endsWith(";")) {
- name = name.substring(2, name.length() - 1);
- }
- // decode internal representation
- name = name.replace('/', '.');
- classes.add(name);
+ public void addName(String name) {
+ if (name == null) {
+ return;
+ }
+ // decode arrays
+ if (name.startsWith("[L") && name.endsWith(";")) {
+ name = name.substring(2, name.length() - 1);
}
+ // decode internal representation
+ name = name.replace('/', '.');
+ classes.add(name);
+ }
- void addMethodDesc(final String desc) {
- addType(Type.getReturnType(desc));
- Type[] types = Type.getArgumentTypes(desc);
- for (Type type : types) {
- addType(type);
- }
+ void addMethodDesc(final String desc) {
+ addType(Type.getReturnType(desc));
+ Type[] types = Type.getArgumentTypes(desc);
+ for (Type type : types) {
+ addType(type);
}
+ }
}
diff --git a/depclean-core/src/main/java/se/kth/depclean/core/analysis/graph/ClassMembersVisitorCounter.java b/depclean-core/src/main/java/se/kth/depclean/core/analysis/graph/ClassMembersVisitorCounter.java
index c51526c7..e12c19a3 100644
--- a/depclean-core/src/main/java/se/kth/depclean/core/analysis/graph/ClassMembersVisitorCounter.java
+++ b/depclean-core/src/main/java/se/kth/depclean/core/analysis/graph/ClassMembersVisitorCounter.java
@@ -19,73 +19,73 @@
public final class ClassMembersVisitorCounter {
- /**
- * Number of types.
- */
- private static long nbVisitedTypes;
-
- /**
- * Number of fields.
- */
- private static long nbVisitedFields;
-
- /**
- * Number of methods.
- */
- private static long nbVisitedMethods;
-
- /**
- * Number of annotations.
- */
- private static long nbVisitedAnnotations;
-
- private ClassMembersVisitorCounter() {
- throw new IllegalStateException("Utility class");
- }
-
- public static void resetClassCounters() {
- nbVisitedTypes = 0;
- nbVisitedFields = 0;
- nbVisitedMethods = 0;
- nbVisitedAnnotations = 0;
- }
-
- public static void markAsNotFoundClassCounters() {
- nbVisitedTypes = -1;
- nbVisitedFields = -1;
- nbVisitedMethods = -1;
- nbVisitedAnnotations = -1;
- }
-
- public static void addVisitedClass() {
- nbVisitedTypes++;
- }
-
- public static void addVisitedField() {
- nbVisitedFields++;
- }
-
- public static void addVisitedMethod() {
- nbVisitedMethods++;
- }
-
- public static void addVisitedAnnotation() {
- nbVisitedAnnotations++;
- }
-
- public static long getNbVisitedTypes() {
- return nbVisitedTypes;
- }
-
- public static long getNbVisitedFields() {
- return nbVisitedFields;
- }
-
- public static long getNbVisitedMethods() {
- return nbVisitedMethods;
- }
-
- public static long getNbVisitedAnnotations() {
- return nbVisitedAnnotations;
- }
+ /**
+ * Number of types.
+ */
+ private static long nbVisitedTypes;
+
+ /**
+ * Number of fields.
+ */
+ private static long nbVisitedFields;
+
+ /**
+ * Number of methods.
+ */
+ private static long nbVisitedMethods;
+
+ /**
+ * Number of annotations.
+ */
+ private static long nbVisitedAnnotations;
+
+ private ClassMembersVisitorCounter() {
+ throw new IllegalStateException("Utility class");
+ }
+
+ public static void resetClassCounters() {
+ nbVisitedTypes = 0;
+ nbVisitedFields = 0;
+ nbVisitedMethods = 0;
+ nbVisitedAnnotations = 0;
+ }
+
+ public static void markAsNotFoundClassCounters() {
+ nbVisitedTypes = -1;
+ nbVisitedFields = -1;
+ nbVisitedMethods = -1;
+ nbVisitedAnnotations = -1;
+ }
+
+ public static void addVisitedClass() {
+ nbVisitedTypes++;
+ }
+
+ public static void addVisitedField() {
+ nbVisitedFields++;
+ }
+
+ public static void addVisitedMethod() {
+ nbVisitedMethods++;
+ }
+
+ public static void addVisitedAnnotation() {
+ nbVisitedAnnotations++;
+ }
+
+ public static long getNbVisitedTypes() {
+ return nbVisitedTypes;
+ }
+
+ public static long getNbVisitedFields() {
+ return nbVisitedFields;
+ }
+
+ public static long getNbVisitedMethods() {
+ return nbVisitedMethods;
+ }
+
+ public static long getNbVisitedAnnotations() {
+ return nbVisitedAnnotations;
+ }
}
diff --git a/depclean-core/src/main/java/se/kth/depclean/core/analysis/graph/DefaultCallGraph.java b/depclean-core/src/main/java/se/kth/depclean/core/analysis/graph/DefaultCallGraph.java
index 8db424bb..e4f74f83 100644
--- a/depclean-core/src/main/java/se/kth/depclean/core/analysis/graph/DefaultCallGraph.java
+++ b/depclean-core/src/main/java/se/kth/depclean/core/analysis/graph/DefaultCallGraph.java
@@ -1,3 +1,5 @@
+package se.kth.depclean.core.analysis.graph;
+
/*
* Copyright (c) 2020, CASTOR Software Research Centre (www.castor.kth.se)
*
@@ -14,88 +16,88 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package se.kth.depclean.core.analysis.graph;
-
-import org.jgrapht.graph.AbstractBaseGraph;
-import org.jgrapht.graph.DefaultDirectedGraph;
-import org.jgrapht.graph.DefaultEdge;
-import org.jgrapht.traverse.DepthFirstIterator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
+import org.jgrapht.graph.AbstractBaseGraph;
+import org.jgrapht.graph.DefaultDirectedGraph;
+import org.jgrapht.graph.DefaultEdge;
+import org.jgrapht.traverse.DepthFirstIterator;
public class DefaultCallGraph {
- private static final AbstractBaseGraph directedGraph = new DefaultDirectedGraph<>(DefaultEdge.class);
- private static final Set projectVertices = new HashSet<>();
- private static final Map> usagesPerClass = new HashMap<>();
+ private static final AbstractBaseGraph directedGraph = new DefaultDirectedGraph<>(
+ DefaultEdge.class);
+ private static final Set projectVertices = new HashSet<>();
+ private static final Map> usagesPerClass = new HashMap<>();
- public static void addEdge(String clazz, Set referencedClassMembers) {
- directedGraph.addVertex(clazz);
- for (String referencedClassMember : referencedClassMembers) {
- if (!directedGraph.containsVertex(referencedClassMember)) {
- directedGraph.addVertex(referencedClassMember);
- }
- directedGraph.addEdge(clazz, referencedClassMember);
- projectVertices.add(clazz);
+ public static void addEdge(String clazz, Set referencedClassMembers) {
+ directedGraph.addVertex(clazz);
+ for (String referencedClassMember : referencedClassMembers) {
+ if (!directedGraph.containsVertex(referencedClassMember)) {
+ directedGraph.addVertex(referencedClassMember);
+ }
+ directedGraph.addEdge(clazz, referencedClassMember);
+ projectVertices.add(clazz);
- // Save the pair [class -> referencedClassMember] for further analysis
- addReferencedClassMember(clazz, referencedClassMember);
- }
+ // Save the pair [class -> referencedClassMember] for further analysis
+ addReferencedClassMember(clazz, referencedClassMember);
}
+ }
- private static void addReferencedClassMember(String clazz, String referencedClassMember) {
- // System.out.println("\t" + clazz + " -> " + referencedClassMember);
- Set s = usagesPerClass.computeIfAbsent(clazz, k -> new HashSet<>());
- s.add(referencedClassMember);
- }
+ private static void addReferencedClassMember(String clazz, String referencedClassMember) {
+ // System.out.println("\t" + clazz + " -> " + referencedClassMember);
+ Set s = usagesPerClass.computeIfAbsent(clazz, k -> new HashSet<>());
+ s.add(referencedClassMember);
+ }
- public static Set referencedClassMembers(Set projectClasses) {
- // System.out.println("project classes: " + projectClasses);
- Set allReferencedClassMembers = new HashSet<>();
- for (String projectClass : projectClasses) {
- allReferencedClassMembers.addAll(traverse(projectClass));
- }
- // System.out.println("All referenced class members: " + allReferencedClassMembers);
- return allReferencedClassMembers;
+ public static Set referencedClassMembers(Set projectClasses) {
+ // System.out.println("project classes: " + projectClasses);
+ Set allReferencedClassMembers = new HashSet<>();
+ for (String projectClass : projectClasses) {
+ allReferencedClassMembers.addAll(traverse(projectClass));
}
+ // System.out.println("All referenced class members: " + allReferencedClassMembers);
+ return allReferencedClassMembers;
+ }
- /**
- * Traverse the graph using DFS.
- * @param start The starting vertex.
- * @return The set of all visited vertices.
- */
- private static Set traverse(String start) {
- Set referencedClassMembers = new HashSet<>();
- Iterator iterator = new DepthFirstIterator<>(directedGraph, start);
- while (iterator.hasNext()) {
- referencedClassMembers.add(iterator.next());
- }
- return referencedClassMembers;
+ /**
+ * Traverse the graph using DFS.
+ *
+ * @param start The starting vertex.
+ * @return The set of all visited vertices.
+ */
+ private static Set traverse(String start) {
+ Set referencedClassMembers = new HashSet<>();
+ Iterator iterator = new DepthFirstIterator<>(directedGraph, start);
+ while (iterator.hasNext()) {
+ referencedClassMembers.add(iterator.next());
}
+ return referencedClassMembers;
+ }
- public AbstractBaseGraph getDirectedGraph() {
- return directedGraph;
- }
+ public AbstractBaseGraph getDirectedGraph() {
+ return directedGraph;
+ }
- public static Set getProjectVertices() {
- return projectVertices;
- }
+ public static Set getProjectVertices() {
+ return projectVertices;
+ }
- public static Set getVertices() {
- return directedGraph.vertexSet();
- }
+ public static Set getVertices() {
+ return directedGraph.vertexSet();
+ }
- public static void cleanDirectedGraph() {
- directedGraph.vertexSet().clear();
- directedGraph.edgeSet().clear();
- }
+ public static void cleanDirectedGraph() {
+ directedGraph.vertexSet().clear();
+ directedGraph.edgeSet().clear();
+ }
- public Map> getUsagesPerClass() {
- return usagesPerClass;
- }
+ public Map> getUsagesPerClass() {
+ return usagesPerClass;
+ }
}
diff --git a/depclean-core/src/test/java/se/kth/depclean/core/analysis/ClassFileVisitorUtilsTest.java b/depclean-core/src/test/java/se/kth/depclean/core/analysis/ClassFileVisitorUtilsTest.java
index 212edf5a..d4057237 100644
--- a/depclean-core/src/test/java/se/kth/depclean/core/analysis/ClassFileVisitorUtilsTest.java
+++ b/depclean-core/src/test/java/se/kth/depclean/core/analysis/ClassFileVisitorUtilsTest.java
@@ -7,16 +7,16 @@
public class ClassFileVisitorUtilsTest {
- @Before
- public void setUp() throws Exception {
- }
+ @Before
+ public void setUp() throws Exception {
+ }
- @After
- public void tearDown() throws Exception {
- }
+ @After
+ public void tearDown() throws Exception {
+ }
- @Test
- public void accept() {
- Assert.assertTrue(true);
- }
+ @Test
+ public void accept() {
+ Assert.assertTrue(true);
+ }
}
\ No newline at end of file
diff --git a/depclean-maven-plugin/pom.xml b/depclean-maven-plugin/pom.xml
index d0b70286..9634fe6d 100644
--- a/depclean-maven-plugin/pom.xml
+++ b/depclean-maven-plugin/pom.xml
@@ -1,106 +1,145 @@
- 4.0.0
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ 4.0.0
-
-
- se.kth.castor
- depclean-parent-pom
- 2.0.1-SNAPSHOT
-
+
+
+ se.kth.castor
+ depclean-parent-pom
+ 2.0.1-SNAPSHOT
+
-
- depclean-maven-plugin
- 2.0.1-SNAPSHOT
- maven-plugin
- depclean-maven-plugin
+
+ depclean-maven-plugin
+ 2.0.1-SNAPSHOT
+ maven-plugin
+ depclean-maven-plugin
-
- target/site/jacoco/jacoco.xml
-
+
+ target/site/jacoco/jacoco.xml
+
- DepClean is a plugin for automatically remove unused dependencies in Maven projects
+ DepClean is a plugin for automatically remove unused dependencies in Maven projects
-
-
-
-
- org.apache.maven.plugins
- maven-plugin-plugin
- 3.6.0
+
+
+
+
+ org.apache.maven.plugins
+ maven-plugin-plugin
+ 3.6.0
+
+ depclean
+ false
+
+
+
+ mojo-descriptor
+ process-classes
+
+ descriptor
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+ 3.1.2
+
+
+ com.puppycrawl.tools
+ checkstyle
+ 8.41
+
+
+
+
- depclean
- false
+
+ error
+ true
+
+ ../checkstyle.xml
+ UTF-8
+ true
+ true
-
-
- mojo-descriptor
- process-classes
-
- descriptor
-
-
-
-
-
-
+ checkstyle
+ validate
+
+ check
+
+
+
+ verify
+
+
+ checkstyle
+
+
+
+
+
+
-
-
-
- se.kth.castor
- depclean-core
- 2.0.1-SNAPSHOT
-
-
-
- org.apache.maven
- maven-core
- 3.6.0
-
-
- org.apache.maven
- maven-plugin-api
- 3.6.0
-
-
- org.apache.maven
- maven-project
- 3.0-alpha-2
-
-
- org.apache.maven.plugin-tools
- maven-plugin-annotations
- 3.6.0
-
-
- org.apache.maven.shared
- maven-dependency-tree
- 3.0.1
-
-
-
- commons-io
- commons-io
- 2.5
-
-
- com.google.code.gson
- gson
- 2.8.6
-
-
- org.whitesource
- maven-dependency-tree-parser
- 1.0.6
-
-
-
- org.apache.maven.plugin-testing
- maven-plugin-testing-tools
- 3.3.0
- compile
-
-
+
+
+
+ se.kth.castor
+ depclean-core
+ 2.0.1-SNAPSHOT
+
+
+
+ org.apache.maven
+ maven-core
+ 3.6.0
+
+
+ org.apache.maven
+ maven-plugin-api
+ 3.6.0
+
+
+ org.apache.maven
+ maven-project
+ 3.0-alpha-2
+
+
+ org.apache.maven.plugin-tools
+ maven-plugin-annotations
+ 3.6.0
+
+
+ org.apache.maven.shared
+ maven-dependency-tree
+ 3.0.1
+
+
+
+ commons-io
+ commons-io
+ 2.5
+
+
+ com.google.code.gson
+ gson
+ 2.8.6
+
+
+ org.whitesource
+ maven-dependency-tree-parser
+ 1.0.6
+
+
+
+ org.apache.maven.plugin-testing
+ maven-plugin-testing-tools
+ 3.3.0
+ compile
+
+
diff --git a/depclean-maven-plugin/src/main/java/se/kth/depclean/DepCleanMojo.java b/depclean-maven-plugin/src/main/java/se/kth/depclean/DepCleanMojo.java
index b6607946..f7bc0b81 100644
--- a/depclean-maven-plugin/src/main/java/se/kth/depclean/DepCleanMojo.java
+++ b/depclean-maven-plugin/src/main/java/se/kth/depclean/DepCleanMojo.java
@@ -18,6 +18,22 @@
package se.kth.depclean;
import fr.dutra.tools.maven.deptree.core.ParseException;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.maven.artifact.Artifact;
@@ -51,639 +67,643 @@
import se.kth.depclean.util.MavenInvoker;
import se.kth.depclean.util.json.ParsedDependencies;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.nio.charset.Charset;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
/**
- * This Maven mojo is the main class of DepClean.
- * DepClean is built on top of the maven-dependency-analyzer component.
+ * This Maven mojo is the main class of DepClean. DepClean is built on top of the maven-dependency-analyzer component.
* It produces a clean copy of the project's pom file, without bloated dependencies.
*
- * @see
- *
- * @see
- *
+ * @see
+ * @see
*/
@Mojo(name = "depclean", defaultPhase = LifecyclePhase.PACKAGE,
- requiresDependencyCollection = ResolutionScope.TEST,
- requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true)
+ requiresDependencyCollection = ResolutionScope.TEST,
+ requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true)
@Slf4j
public class DepCleanMojo extends AbstractMojo {
- private static final String SEPARATOR = "-------------------------------------------------------";
- public static final String DIRECTORY_TO_COPY_DEPENDENCIES = "dependency";
-
- /**
- * The Maven project to analyze.
- */
- @Parameter(defaultValue = "${project}", readonly = true)
- private MavenProject project;
-
- /**
- * The Maven session to analyze.
- */
- @Parameter(defaultValue = "${session}", readonly = true)
- private MavenSession session;
-
- /**
- * If this is true, DepClean creates a debloated version of the pom without unused dependencies,
- * called "debloated-pom.xml", in root of the project.
- */
- @Parameter(property = "creatPomDebloated", defaultValue = "false")
- private boolean createPomDebloated;
-
- /**
- * If this is true, DepClean creates a JSON file with the result of the analysis. The file is called
- * "debloat-result.json" and it is located in the root of the project.
- */
- @Parameter(property = "createResultJson", defaultValue = "false")
- private boolean createResultJson;
-
- /**
- * Add a list of dependencies, identified by their coordinates, to be ignored by DepClean during the analysis and
- * considered as used dependencies. Useful to override incomplete result caused by bytecode-level analysis
- * Dependency format is groupId:artifactId:version
.
- */
- @Parameter(property = "ignoreDependencies")
- private Set ignoreDependencies;
-
- /**
- * Ignore dependencies with specific scopes from the DepClean analysis.
- */
- @Parameter(property = "ignoreScopes")
- private Set ignoreScopes;
-
- /**
- * If this is true, DepClean will not analyze the test sources in the project,
- * and, therefore, the dependencies that are only used for testing will be considered
- * unused. This property is useful to detect dependencies that have a compile scope but
- * are only used during testing. Hence, these dependencies should have a test scope.
- */
- @Parameter(property = "ignoreTests", defaultValue = "false")
- private boolean ignoreTests;
-
- /**
- * If this is true, and DepClean reported any unused direct dependency in the dependency tree,
- * then the project's build fails immediately after running DepClean.
- */
- @Parameter(property = "failIfUnusedDirect", defaultValue = "false")
- private boolean failIfUnusedDirect;
-
- /**
- * If this is true, and DepClean reported any unused transitive dependency in the dependency tree,
- * then the project's build fails immediately after running DepClean.
- */
- @Parameter(property = "failIfUnusedTransitive", defaultValue = "false")
- private boolean failIfUnusedTransitive;
-
- /**
- * If this is true, and DepClean reported any unused inherited dependency in the dependency tree,
- * then the project's build fails immediately after running DepClean.
- */
- @Parameter(property = "failIfUnusedInherited", defaultValue = "false")
- private boolean failIfUnusedInherited;
-
- /**
- * Skip plugin execution completely.
- */
- @Parameter(property = "skipDepClean", defaultValue = "false")
- private boolean skipDepClean;
-
- @Component
- private ProjectBuilder mavenProjectBuilder;
-
- @Component
- private RepositorySystem repositorySystem;
-
- @Component(hint = "default")
- private DependencyGraphBuilder dependencyGraphBuilder;
-
- /**
- * Write pom file to the filesystem.
- *
- * @param pomFile The path to the pom.
- * @param model The maven model to get the pom from.
- * @throws IOException In case of any IO issue.
- */
- private static void writePom(final Path pomFile, final Model model) throws IOException {
- MavenXpp3Writer writer = new MavenXpp3Writer();
- writer.write(Files.newBufferedWriter(pomFile), model);
- }
-
- /**
- * Print the status of the depenencies to the standard output.
- * The format is: "[coordinates][scope] [(size)]"
- *
- * @param sizeOfDependencies A map with the size of the dependencies.
- * @param dependencies The set dependencies to print.
- */
- private void printDependencies(Map sizeOfDependencies, Set dependencies) {
- dependencies
- .stream()
- .sorted(Comparator.comparing(o -> getSizeOfDependency(sizeOfDependencies, o)))
- .collect(Collectors.toCollection(LinkedList::new))
- .descendingIterator()
- .forEachRemaining(s -> printString("\t" + s + " (" + getSize(s, sizeOfDependencies) + ")"));
- }
-
- /**
- * Utility method to obtain the size of a dependency from a map of dependency -> size. If the size of the dependency
- * cannot be obtained form the map (no key with the name of the dependency exists), then it returns 0.
- *
- * @param sizeOfDependencies A map of dependency -> size.
- * @param dependency The coordinates of a dependency.
- * @return The size of the dependency if its name is a key in the map, otherwise it returns 0.
- */
- private Long getSizeOfDependency(Map sizeOfDependencies, String dependency) {
- Long size = sizeOfDependencies.get(dependency.split(":")[1] + "-" + dependency.split(":")[2] + ".jar");
- if (size != null) {
- return size;
- } else {
- // The name of the dependency does not match with the name of the download jar, so we keep assume the size
- // cannot be obtained and return 0.
- return Long.valueOf(0);
- }
- }
-
- /**
- * Get the size of the dependency in human readable format.
- *
- * @param dependency The dependency.
- * @param sizeOfDependencies A map with the size of the dependencies, keys are stored as the downloaded jar file
- * i.e., [artifactId]-[version].jar
- * @return The human readable representation of the dependency size.
- */
- private String getSize(String dependency, Map sizeOfDependencies) {
- String dep = dependency.split(":")[1] + "-" + dependency.split(":")[2] + ".jar";
- if (sizeOfDependencies.containsKey(dep)) {
- return FileUtils.byteCountToDisplaySize(sizeOfDependencies.get(dep));
- } else {
- // The size cannot be obtained.
- return "size unknown";
+ private static final String SEPARATOR = "-------------------------------------------------------";
+ public static final String DIRECTORY_TO_COPY_DEPENDENCIES = "dependency";
+
+ /**
+ * The Maven project to analyze.
+ */
+ @Parameter(defaultValue = "${project}", readonly = true)
+ private MavenProject project;
+
+ /**
+ * The Maven session to analyze.
+ */
+ @Parameter(defaultValue = "${session}", readonly = true)
+ private MavenSession session;
+
+ /**
+ * If this is true, DepClean creates a debloated version of the pom without unused dependencies, called
+ * "debloated-pom.xml", in root of the project.
+ */
+ @Parameter(property = "creatPomDebloated", defaultValue = "false")
+ private boolean createPomDebloated;
+
+ /**
+ * If this is true, DepClean creates a JSON file with the result of the analysis. The file is called
+ * "debloat-result.json" and it is located in the root of the project.
+ */
+ @Parameter(property = "createResultJson", defaultValue = "false")
+ private boolean createResultJson;
+
+ /**
+ * Add a list of dependencies, identified by their coordinates, to be ignored by DepClean during the analysis and
+ * considered as used dependencies. Useful to override incomplete result caused by bytecode-level analysis Dependency
+ * format is groupId:artifactId:version
.
+ */
+ @Parameter(property = "ignoreDependencies")
+ private Set ignoreDependencies;
+
+ /**
+ * Ignore dependencies with specific scopes from the DepClean analysis.
+ */
+ @Parameter(property = "ignoreScopes")
+ private Set ignoreScopes;
+
+ /**
+ * If this is true, DepClean will not analyze the test sources in the project, and, therefore, the dependencies that
+ * are only used for testing will be considered unused. This property is useful to detect dependencies that have a
+ * compile scope but are only used during testing. Hence, these dependencies should have a test scope.
+ */
+ @Parameter(property = "ignoreTests", defaultValue = "false")
+ private boolean ignoreTests;
+
+ /**
+ * If this is true, and DepClean reported any unused direct dependency in the dependency tree, then the project's
+ * build fails immediately after running DepClean.
+ */
+ @Parameter(property = "failIfUnusedDirect", defaultValue = "false")
+ private boolean failIfUnusedDirect;
+
+ /**
+ * If this is true, and DepClean reported any unused transitive dependency in the dependency tree, then the project's
+ * build fails immediately after running DepClean.
+ */
+ @Parameter(property = "failIfUnusedTransitive", defaultValue = "false")
+ private boolean failIfUnusedTransitive;
+
+ /**
+ * If this is true, and DepClean reported any unused inherited dependency in the dependency tree, then the project's
+ * build fails immediately after running DepClean.
+ */
+ @Parameter(property = "failIfUnusedInherited", defaultValue = "false")
+ private boolean failIfUnusedInherited;
+
+ /**
+ * Skip plugin execution completely.
+ */
+ @Parameter(property = "skipDepClean", defaultValue = "false")
+ private boolean skipDepClean;
+
+ @Component
+ private ProjectBuilder mavenProjectBuilder;
+
+ @Component
+ private RepositorySystem repositorySystem;
+
+ @Component(hint = "default")
+ private DependencyGraphBuilder dependencyGraphBuilder;
+
+ /**
+ * Write pom file to the filesystem.
+ *
+ * @param pomFile The path to the pom.
+ * @param model The maven model to get the pom from.
+ * @throws IOException In case of any IO issue.
+ */
+ private static void writePom(final Path pomFile, final Model model) throws IOException {
+ MavenXpp3Writer writer = new MavenXpp3Writer();
+ writer.write(Files.newBufferedWriter(pomFile), model);
+ }
+
+ /**
+ * Print the status of the depenencies to the standard output. The format is: "[coordinates][scope] [(size)]"
+ *
+ * @param sizeOfDependencies A map with the size of the dependencies.
+ * @param dependencies The set dependencies to print.
+ */
+ private void printDependencies(Map sizeOfDependencies, Set dependencies) {
+ dependencies
+ .stream()
+ .sorted(Comparator.comparing(o -> getSizeOfDependency(sizeOfDependencies, o)))
+ .collect(Collectors.toCollection(LinkedList::new))
+ .descendingIterator()
+ .forEachRemaining(s -> printString("\t" + s + " (" + getSize(s, sizeOfDependencies) + ")"));
+ }
+
+ /**
+ * Utility method to obtain the size of a dependency from a map of dependency -> size. If the size of the dependency
+ * cannot be obtained form the map (no key with the name of the dependency exists), then it returns 0.
+ *
+ * @param sizeOfDependencies A map of dependency -> size.
+ * @param dependency The coordinates of a dependency.
+ * @return The size of the dependency if its name is a key in the map, otherwise it returns 0.
+ */
+ private Long getSizeOfDependency(Map sizeOfDependencies, String dependency) {
+ Long size = sizeOfDependencies
+ .get(dependency.split(":")[1] + "-" + dependency.split(":")[2] + ".jar");
+ if (size != null) {
+ return size;
+ } else {
+ // The name of the dependency does not match with the name of the download jar, so we keep assume the size
+ // cannot be obtained and return 0.
+ return Long.valueOf(0);
+ }
+ }
+
+ /**
+ * Get the size of the dependency in human readable format.
+ *
+ * @param dependency The dependency.
+ * @param sizeOfDependencies A map with the size of the dependencies, keys are stored as the downloaded jar file i.e.,
+ * [artifactId]-[version].jar
+ * @return The human readable representation of the dependency size.
+ */
+ private String getSize(String dependency, Map sizeOfDependencies) {
+ String dep = dependency.split(":")[1] + "-" + dependency.split(":")[2] + ".jar";
+ if (sizeOfDependencies.containsKey(dep)) {
+ return FileUtils.byteCountToDisplaySize(sizeOfDependencies.get(dep));
+ } else {
+ // The size cannot be obtained.
+ return "size unknown";
+ }
+ }
+
+ /**
+ * Exclude artifacts with specific scopes from the analysis.
+ *
+ * @param artifacts The set of artifacts to analyze.
+ * @return The set of artifacts for which the scope has not been excluded.
+ */
+ private Set excludeScope(Set artifacts) {
+ Set nonExcludedArtifacts = new HashSet<>();
+ for (Artifact artifact : artifacts) {
+ if (!ignoreScopes.contains(artifact.getScope())) {
+ nonExcludedArtifacts.add(artifact);
}
- }
-
- /**
- * Exclude artifacts with specific scopes from the analysis.
- *
- * @param artifacts The set of artifacts to analyze.
- * @return The set of artifacts for which the scope has not been excluded.
- */
- private Set excludeScope(Set artifacts) {
- Set nonExcludedArtifacts = new HashSet<>();
- for (Artifact artifact : artifacts) {
- if (!ignoreScopes.contains(artifact.getScope())) {
- nonExcludedArtifacts.add(artifact);
- }
+ }
+ return nonExcludedArtifacts;
+ }
+
+ /**
+ * Determine if an artifact is a direct or transitive child of a dependency.
+ *
+ * @param artifact The artifact.
+ * @param dependency The dependency
+ * @return true if the artifact is a child of a dependency in the dependency tree.
+ * @throws DependencyGraphBuilderException If the graph cannot be constructed.
+ */
+ private boolean isChildren(Artifact artifact, Dependency dependency)
+ throws DependencyGraphBuilderException {
+ List dependencyNodes = getDependencyNodes();
+ for (DependencyNode node : dependencyNodes) {
+ Dependency dependencyNode = createDependency(node.getArtifact());
+ if (dependency.getGroupId().equals(dependencyNode.getGroupId())
+ && dependency.getArtifactId().equals(dependencyNode.getArtifactId())) {
+ // now we are in the target dependency
+ for (DependencyNode child : node.getChildren()) {
+ if (child.getArtifact().equals(artifact)) {
+ // the dependency contains the artifact as a child node
+ return true;
+ }
+
+ }
}
- return nonExcludedArtifacts;
- }
-
- /**
- * Determine if an artifact is a direct or transitive child of a dependency.
- *
- * @param artifact The artifact.
- * @param dependency The dependency
- * @return true if the artifact is a child of a dependency in the dependency tree.
- * @throws DependencyGraphBuilderException If the graph cannot be constructed.
- */
- private boolean isChildren(Artifact artifact, Dependency dependency) throws DependencyGraphBuilderException {
- List dependencyNodes = getDependencyNodes();
- for (DependencyNode node : dependencyNodes) {
- Dependency dependencyNode = createDependency(node.getArtifact());
- if (dependency.getGroupId().equals(dependencyNode.getGroupId()) &&
- dependency.getArtifactId().equals(dependencyNode.getArtifactId())) {
- // now we are in the target dependency
- for (DependencyNode child : node.getChildren()) {
- if (child.getArtifact().equals(artifact)) {
- // the dependency contains the artifact as a child node
- return true;
- }
-
- }
- }
- }
- return false;
- }
-
- /**
- * This method returns a list of dependency nodes from a graph of dependency tree.
- *
- * @return The nodes in the dependency graph.
- * @throws DependencyGraphBuilderException If the graph cannot be built.
- */
- private List getDependencyNodes() throws DependencyGraphBuilderException {
- ProjectBuildingRequest buildingRequest = new DefaultProjectBuildingRequest(session.getProjectBuildingRequest());
- buildingRequest.setProject(project);
- DependencyNode rootNode = dependencyGraphBuilder.buildDependencyGraph(buildingRequest, null);
- CollectingDependencyNodeVisitor visitor = new CollectingDependencyNodeVisitor();
- rootNode.accept(visitor);
- return visitor.getNodes();
- }
-
- /**
- * This method creates a {@link org.apache.maven.model.Dependency} object from a
- * Maven {@link org.apache.maven.artifact.Artifact}.
- *
- * @param artifact The artifact to create the dependency.
- * @return The Dependency object.
- */
- private Dependency createDependency(final Artifact artifact) {
- Dependency dependency = new Dependency();
- dependency.setGroupId(artifact.getGroupId());
- dependency.setArtifactId(artifact.getArtifactId());
- dependency.setVersion(artifact.getVersion());
- if (artifact.hasClassifier()) {
- dependency.setClassifier(artifact.getClassifier());
- }
- dependency.setOptional(artifact.isOptional());
- dependency.setScope(artifact.getScope());
- dependency.setType(artifact.getType());
- return dependency;
- }
-
- private void printString(String string) {
- System.out.println(string); //NOSONAR avoid a warning of non-used logger
- }
-
- @Override
- public void execute() throws MojoExecutionException, MojoFailureException {
- if (skipDepClean) {
- getLog().info("Skipping DepClean plugin execution");
- return;
- }
-
- printString(SEPARATOR);
- getLog().info("Starting DepClean dependency analysis");
-
- File pomFile = new File(project.getBasedir().getAbsolutePath() + File.separator + "pom.xml");
-
- String packaging = project.getPackaging();
- if (packaging.equals("pom")) {
- getLog().info("Skipping because packaging type " + packaging + ".");
- return;
- }
-
- /* Build Maven model to manipulate the pom */
- Model model;
- FileReader reader;
- MavenXpp3Reader mavenReader = new MavenXpp3Reader();
+ }
+ return false;
+ }
+
+ /**
+ * This method returns a list of dependency nodes from a graph of dependency tree.
+ *
+ * @return The nodes in the dependency graph.
+ * @throws {@link org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException} if the graph cannot be
+ * built.
+ */
+ private List getDependencyNodes() throws DependencyGraphBuilderException {
+ ProjectBuildingRequest buildingRequest = new DefaultProjectBuildingRequest(
+ session.getProjectBuildingRequest());
+ buildingRequest.setProject(project);
+ DependencyNode rootNode = dependencyGraphBuilder.buildDependencyGraph(buildingRequest, null);
+ CollectingDependencyNodeVisitor visitor = new CollectingDependencyNodeVisitor();
+ rootNode.accept(visitor);
+ return visitor.getNodes();
+ }
+
+ /**
+ * This method creates a {@link org.apache.maven.model.Dependency} object from a Maven {@link
+ * org.apache.maven.artifact.Artifact}.
+ *
+ * @param artifact The artifact to create the dependency.
+ * @return The Dependency object.
+ */
+ private Dependency createDependency(final Artifact artifact) {
+ Dependency dependency = new Dependency();
+ dependency.setGroupId(artifact.getGroupId());
+ dependency.setArtifactId(artifact.getArtifactId());
+ dependency.setVersion(artifact.getVersion());
+ if (artifact.hasClassifier()) {
+ dependency.setClassifier(artifact.getClassifier());
+ }
+ dependency.setOptional(artifact.isOptional());
+ dependency.setScope(artifact.getScope());
+ dependency.setType(artifact.getType());
+ return dependency;
+ }
+
+ private void printString(String string) {
+ System.out.println(string); //NOSONAR avoid a warning of non-used logger
+ }
+
+ @Override
+ public void execute() throws MojoExecutionException, MojoFailureException {
+ if (skipDepClean) {
+ getLog().info("Skipping DepClean plugin execution");
+ return;
+ }
+
+ printString(SEPARATOR);
+ getLog().info("Starting DepClean dependency analysis");
+
+ File pomFile = new File(project.getBasedir().getAbsolutePath() + File.separator + "pom.xml");
+
+ String packaging = project.getPackaging();
+ if (packaging.equals("pom")) {
+ getLog().info("Skipping because packaging type " + packaging + ".");
+ return;
+ }
+
+ /* Build Maven model to manipulate the pom */
+ Model model;
+ FileReader reader;
+ MavenXpp3Reader mavenReader = new MavenXpp3Reader();
+ try {
+ reader = new FileReader(pomFile);
+ model = mavenReader.read(reader);
+ model.setPomFile(pomFile);
+ } catch (Exception ex) {
+ getLog().error("Unable to build the maven project.");
+ return;
+ }
+
+ /* Copy direct dependencies locally */
+ try {
+ MavenInvoker.runCommand("mvn dependency:copy-dependencies -DoutputDirectory="
+ + project.getBuild().getDirectory() + File.separator + DIRECTORY_TO_COPY_DEPENDENCIES);
+ } catch (IOException | InterruptedException e) {
+ getLog().error("Unable to resolve all the dependencies.");
+ Thread.currentThread().interrupt();
+ return;
+ }
+
+ // TODO remove this workaround later
+ if (new File(project.getBuild().getDirectory() + File.separator + "libs").exists()) {
try {
- reader = new FileReader(pomFile);
- model = mavenReader.read(reader);
- model.setPomFile(pomFile);
- } catch (Exception ex) {
- getLog().error("Unable to build the maven project.");
- return;
+ FileUtils.copyDirectory(new File(project.getBuild().getDirectory() + File.separator + "libs"),
+ new File(project.getBuild().getDirectory() + File.separator + DIRECTORY_TO_COPY_DEPENDENCIES)
+ );
+ } catch (IOException e) {
+ getLog().error("Error copying directory libs to dependency");
}
-
- /* Copy direct dependencies locally */
- try {
- MavenInvoker.runCommand("mvn dependency:copy-dependencies -DoutputDirectory=" +
- project.getBuild().getDirectory() + File.separator + DIRECTORY_TO_COPY_DEPENDENCIES);
- } catch (IOException | InterruptedException e) {
- getLog().error("Unable to resolve all the dependencies.");
- Thread.currentThread().interrupt();
- return;
+ }
+
+ /* Get the size of all the dependencies */
+ Map sizeOfDependencies = new HashMap<>();
+ // First, add the size of the project, as the sum of all the files in target/classes
+ String projectJar = project.getArtifactId() + "-" + project.getVersion() + ".jar";
+ long projectSize = FileUtils.sizeOf(new File(project.getBuild().getOutputDirectory()));
+ sizeOfDependencies.put(projectJar, projectSize);
+ if (Files.exists(Path.of(
+ project.getBuild().getDirectory() + File.separator + DIRECTORY_TO_COPY_DEPENDENCIES))) {
+ Iterator iterator = FileUtils.iterateFiles(
+ new File(
+ project.getBuild().getDirectory() + File.separator
+ + DIRECTORY_TO_COPY_DEPENDENCIES), new String[]{"jar"}, true);
+ while (iterator.hasNext()) {
+ File file = iterator.next();
+ sizeOfDependencies.put(file.getName(), FileUtils.sizeOf(file));
}
-
- // TODO remove this workaround later
- if (new File(project.getBuild().getDirectory() + File.separator + "libs").exists()) {
- try {
- FileUtils.copyDirectory(new File(project.getBuild().getDirectory() + File.separator + "libs"),
- new File(project.getBuild().getDirectory() + File.separator + DIRECTORY_TO_COPY_DEPENDENCIES)
- );
- } catch (IOException e) {
- getLog().error("Error copying directory libs to dependency");
- }
+ } else {
+ log.warn("Dependencies where not copied locally");
+ }
+
+ /* Decompress dependencies */
+ String dependencyDirectoryName =
+ project.getBuild().getDirectory() + "/" + DIRECTORY_TO_COPY_DEPENDENCIES;
+ File dependencyDirectory = new File(dependencyDirectoryName);
+ if (dependencyDirectory.exists()) {
+ JarUtils.decompressJars(dependencyDirectoryName);
+ }
+
+ /* Analyze dependencies usage status */
+ ProjectDependencyAnalysis projectDependencyAnalysis;
+ DefaultProjectDependencyAnalyzer dependencyAnalyzer = new DefaultProjectDependencyAnalyzer(
+ ignoreTests);
+ try {
+ projectDependencyAnalysis = dependencyAnalyzer.analyze(project);
+ } catch (ProjectDependencyAnalyzerException e) {
+ getLog().error("Unable to analyze dependencies.");
+ return;
+ }
+
+ Set usedTransitiveArtifacts = projectDependencyAnalysis.getUsedUndeclaredArtifacts();
+ Set usedDirectArtifacts = projectDependencyAnalysis.getUsedDeclaredArtifacts();
+ Set unusedDirectArtifacts = projectDependencyAnalysis.getUnusedDeclaredArtifacts();
+ Set unusedTransitiveArtifacts = project.getArtifacts();
+
+ unusedTransitiveArtifacts.removeAll(usedDirectArtifacts);
+ unusedTransitiveArtifacts.removeAll(usedTransitiveArtifacts);
+ unusedTransitiveArtifacts.removeAll(unusedDirectArtifacts);
+
+ /* Exclude dependencies with specific scopes from the DepClean analysis */
+ if (!ignoreScopes.isEmpty()) {
+ printString("Ignoring dependencies with scope(s): " + ignoreScopes.toString());
+ if (!ignoreScopes.isEmpty()) {
+ usedTransitiveArtifacts = excludeScope(usedTransitiveArtifacts);
+ usedDirectArtifacts = excludeScope(usedDirectArtifacts);
+ unusedDirectArtifacts = excludeScope(unusedDirectArtifacts);
+ unusedTransitiveArtifacts = excludeScope(unusedTransitiveArtifacts);
}
-
- /* Get the size of all the dependencies */
- Map sizeOfDependencies = new HashMap<>();
- // First, add the size of the project, as the sum of all the files in target/classes
- String projectJar = project.getArtifactId() + "-" + project.getVersion() + ".jar";
- long projectSize = FileUtils.sizeOf(new File(project.getBuild().getOutputDirectory()));
- sizeOfDependencies.put(projectJar, projectSize);
- if (Files.exists(Path.of(project.getBuild().getDirectory() + File.separator + DIRECTORY_TO_COPY_DEPENDENCIES))) {
- Iterator iterator = FileUtils.iterateFiles(
- new File(
- project.getBuild().getDirectory() + File.separator
- + DIRECTORY_TO_COPY_DEPENDENCIES), new String[]{"jar"}, true);
- while (iterator.hasNext()) {
- File file = iterator.next();
- sizeOfDependencies.put(file.getName(), FileUtils.sizeOf(file));
- }
+ }
+
+ /* Use artifacts coordinates for the report instead of the Artifact object */
+
+ // List of dependencies declared in the POM
+ List dependencies = model.getDependencies();
+ Set declaredArtifactsGAs = new HashSet<>();
+ for (Dependency dep : dependencies) {
+ declaredArtifactsGAs.add(dep.getGroupId() + ":" + dep.getArtifactId());
+ }
+
+ // --- used dependencies
+ Set usedDirectArtifactsCoordinates = new HashSet<>();
+ Set usedInheritedArtifactsCoordinates = new HashSet<>();
+ Set usedTransitiveArtifactsCoordinates = new HashSet<>();
+
+ for (Artifact artifact : usedDirectArtifacts) {
+ String artifactGA = artifact.getGroupId() + ":" + artifact.getArtifactId();
+ String artifactGAVS =
+ artifactGA + ":" + artifact.toString().split(":")[3] + ":" + artifact.toString()
+ .split(":")[4];
+ if (declaredArtifactsGAs.contains(artifactGA)) {
+ // the artifact is declared in the pom
+ usedDirectArtifactsCoordinates.add(artifactGAVS);
} else {
- log.warn("Dependencies where not copied locally");
+ // the artifact is inherited
+ usedInheritedArtifactsCoordinates.add(artifactGAVS);
}
-
- /* Decompress dependencies */
- String dependencyDirectoryName = project.getBuild().getDirectory() + "/" + DIRECTORY_TO_COPY_DEPENDENCIES;
- File dependencyDirectory = new File(dependencyDirectoryName);
- if (dependencyDirectory.exists()) {
- JarUtils.decompressJars(dependencyDirectoryName);
+ }
+
+ // TODO Fix: The used transitive dependencies induced by inherited dependencies should be considered
+ // as used inherited
+ for (Artifact artifact : usedTransitiveArtifacts) {
+ String artifactGA = artifact.getGroupId() + ":" + artifact.getArtifactId();
+ String artifactGAVS =
+ artifactGA + ":" + artifact.toString().split(":")[3] + ":" + artifact.toString()
+ .split(":")[4];
+ usedTransitiveArtifactsCoordinates.add(artifactGAVS);
+ }
+
+ // --- unused dependencies
+ Set unusedDirectArtifactsCoordinates = new HashSet<>();
+ Set unusedInheritedArtifactsCoordinates = new HashSet<>();
+ Set unusedTransitiveArtifactsCoordinates = new HashSet<>();
+
+ for (Artifact artifact : unusedDirectArtifacts) {
+ String artifactGA = artifact.getGroupId() + ":" + artifact.getArtifactId();
+ String artifactGAVS =
+ artifactGA + ":" + artifact.toString().split(":")[3] + ":" + artifact.toString()
+ .split(":")[4];
+ if (declaredArtifactsGAs.contains(artifactGA)) {
+ // the artifact is declared in the pom
+ unusedDirectArtifactsCoordinates.add(artifactGAVS);
+ } else {
+ // the artifact is inherited
+ unusedInheritedArtifactsCoordinates.add(artifactGAVS);
}
-
- /* Analyze dependencies usage status */
- ProjectDependencyAnalysis projectDependencyAnalysis;
- DefaultProjectDependencyAnalyzer dependencyAnalyzer = new DefaultProjectDependencyAnalyzer(ignoreTests);
- try {
- projectDependencyAnalysis = dependencyAnalyzer.analyze(project);
- } catch (ProjectDependencyAnalyzerException e) {
- getLog().error("Unable to analyze dependencies.");
- return;
+ }
+
+ // TODO Fix: The unused transitive dependencies induced by inherited dependencies should be considered as
+ // unused inherited
+ for (Artifact artifact : unusedTransitiveArtifacts) {
+ String artifactGA = artifact.getGroupId() + ":" + artifact.getArtifactId();
+ String artifactGAVS =
+ artifactGA + ":" + artifact.toString().split(":")[3] + ":" + artifact.toString()
+ .split(":")[4];
+ unusedTransitiveArtifactsCoordinates.add(artifactGAVS);
+ }
+
+ /* Ignoring dependencies from the analysis */
+ if (ignoreDependencies != null) {
+ for (String ignoredDependency : ignoreDependencies) {
+ // if the ignored dependency is an unused direct dependency then add it to the set of used direct
+ // and remove it from the set of unused direct
+ for (Iterator i = unusedDirectArtifactsCoordinates.iterator(); i.hasNext(); ) {
+ String unusedDirectArtifact = i.next();
+ if (ignoredDependency.equals(unusedDirectArtifact)) {
+ usedDirectArtifactsCoordinates.add(unusedDirectArtifact);
+ i.remove();
+ break;
+ }
+ }
+ // if the ignored dependency is an unused inherited dependency then add it to the set of used inherited
+ // and remove it from the set of unused inherited
+ for (Iterator j = unusedInheritedArtifactsCoordinates.iterator(); j.hasNext(); ) {
+ String unusedInheritedArtifact = j.next();
+ if (ignoredDependency.equals(unusedInheritedArtifact)) {
+ usedInheritedArtifactsCoordinates.add(unusedInheritedArtifact);
+ j.remove();
+ break;
+ }
+ }
+ // if the ignored dependency is an unused transitive dependency then add it to the set of used transitive
+ // and remove it from the set of unused transitive
+ for (Iterator j = unusedTransitiveArtifactsCoordinates.iterator(); j.hasNext(); ) {
+ String unusedTransitiveArtifact = j.next();
+ if (ignoredDependency.equals(unusedTransitiveArtifact)) {
+ usedTransitiveArtifactsCoordinates.add(unusedTransitiveArtifact);
+ j.remove();
+ break;
+ }
+ }
}
+ }
- Set usedTransitiveArtifacts = projectDependencyAnalysis.getUsedUndeclaredArtifacts();
- Set usedDirectArtifacts = projectDependencyAnalysis.getUsedDeclaredArtifacts();
- Set unusedDirectArtifacts = projectDependencyAnalysis.getUnusedDeclaredArtifacts();
- Set unusedTransitiveArtifacts = project.getArtifacts();
+ /* Printing the results to the console */
+ printString(SEPARATOR);
+ printString(" D E P C L E A N A N A L Y S I S R E S U L T S");
+ printString(SEPARATOR);
- unusedTransitiveArtifacts.removeAll(usedDirectArtifacts);
- unusedTransitiveArtifacts.removeAll(usedTransitiveArtifacts);
- unusedTransitiveArtifacts.removeAll(unusedDirectArtifacts);
+ printString("Used direct dependencies".toUpperCase()
+ + " [" + usedDirectArtifactsCoordinates.size() + "]" + ": ");
+ printDependencies(sizeOfDependencies, usedDirectArtifactsCoordinates);
- /* Exclude dependencies with specific scopes from the DepClean analysis */
- if (!ignoreScopes.isEmpty()) {
- printString("Ignoring dependencies with scope(s): " + ignoreScopes.toString());
- if (!ignoreScopes.isEmpty()) {
- usedTransitiveArtifacts = excludeScope(usedTransitiveArtifacts);
- usedDirectArtifacts = excludeScope(usedDirectArtifacts);
- unusedDirectArtifacts = excludeScope(unusedDirectArtifacts);
- unusedTransitiveArtifacts = excludeScope(unusedTransitiveArtifacts);
- }
- }
+ printString("Used inherited dependencies".toUpperCase()
+ + " [" + usedInheritedArtifactsCoordinates.size() + "]" + ": ");
+ printDependencies(sizeOfDependencies, usedInheritedArtifactsCoordinates);
- /* Use artifacts coordinates for the report instead of the Artifact object */
+ printString("Used transitive dependencies".toUpperCase()
+ + " [" + usedTransitiveArtifactsCoordinates.size() + "]" + ": ");
+ printDependencies(sizeOfDependencies, usedTransitiveArtifactsCoordinates);
- // List of dependencies declared in the POM
- List dependencies = model.getDependencies();
- Set declaredArtifactsGAs = new HashSet<>();
- for (Dependency dep : dependencies) {
- declaredArtifactsGAs.add(dep.getGroupId() + ":" + dep.getArtifactId());
- }
+ printString("Potentially unused direct dependencies".toUpperCase()
+ + " [" + unusedDirectArtifactsCoordinates.size() + "]" + ": ");
+ printDependencies(sizeOfDependencies, unusedDirectArtifactsCoordinates);
- // --- used dependencies
- Set usedDirectArtifactsCoordinates = new HashSet<>();
- Set usedInheritedArtifactsCoordinates = new HashSet<>();
- Set usedTransitiveArtifactsCoordinates = new HashSet<>();
-
- for (Artifact artifact : usedDirectArtifacts) {
- String artifactGA = artifact.getGroupId() + ":" + artifact.getArtifactId();
- String artifactGAVS =
- artifactGA + ":" + artifact.toString().split(":")[3] + ":" + artifact.toString().split(":")[4];
- if (declaredArtifactsGAs.contains(artifactGA)) {
- // the artifact is declared in the pom
- usedDirectArtifactsCoordinates.add(artifactGAVS);
- } else {
- // the artifact is inherited
- usedInheritedArtifactsCoordinates.add(artifactGAVS);
- }
- }
+ printString("Potentially unused inherited dependencies".toUpperCase()
+ + " [" + unusedInheritedArtifactsCoordinates.size() + "]" + ": ");
+ printDependencies(sizeOfDependencies, unusedInheritedArtifactsCoordinates);
- // TODO Fix: The used transitive dependencies induced by inherited dependencies should be considered as used
- // inherited
- for (Artifact artifact : usedTransitiveArtifacts) {
- String artifactGA = artifact.getGroupId() + ":" + artifact.getArtifactId();
- String artifactGAVS =
- artifactGA + ":" + artifact.toString().split(":")[3] + ":" + artifact.toString().split(":")[4];
- usedTransitiveArtifactsCoordinates.add(artifactGAVS);
- }
+ printString("Potentially unused transitive dependencies".toUpperCase()
+ + " [" + unusedTransitiveArtifactsCoordinates.size() + "]" + ": ");
+ printDependencies(sizeOfDependencies, unusedTransitiveArtifactsCoordinates);
- // --- unused dependencies
- Set unusedDirectArtifactsCoordinates = new HashSet<>();
- Set unusedInheritedArtifactsCoordinates = new HashSet<>();
- Set unusedTransitiveArtifactsCoordinates = new HashSet<>();
-
- for (Artifact artifact : unusedDirectArtifacts) {
- String artifactGA = artifact.getGroupId() + ":" + artifact.getArtifactId();
- String artifactGAVS =
- artifactGA + ":" + artifact.toString().split(":")[3] + ":" + artifact.toString().split(":")[4];
- if (declaredArtifactsGAs.contains(artifactGA)) {
- // the artifact is declared in the pom
- unusedDirectArtifactsCoordinates.add(artifactGAVS);
- } else {
- // the artifact is inherited
- unusedInheritedArtifactsCoordinates.add(artifactGAVS);
- }
+ if (!ignoreDependencies.isEmpty()) {
+ printString(SEPARATOR);
+ printString(
+ "Dependencies ignored in the analysis by the user"
+ + " [" + ignoreDependencies.size() + "]" + ":" + " ");
+ ignoreDependencies.stream().forEach(s -> printString("\t" + s));
+ }
+
+ /* Fail the build if there are unused direct dependencies */
+ if (failIfUnusedDirect && !unusedDirectArtifactsCoordinates.isEmpty()) {
+ throw new MojoExecutionException(
+ "Build failed due to unused direct dependencies in the dependency tree of the project.");
+ }
+
+ /* Fail the build if there are unused direct dependencies */
+ if (failIfUnusedTransitive && !unusedTransitiveArtifactsCoordinates.isEmpty()) {
+ throw new MojoExecutionException(
+ "Build failed due to unused transitive dependencies in the dependency tree of the project.");
+ }
+
+ /* Fail the build if there are unused direct dependencies */
+ if (failIfUnusedInherited && !unusedInheritedArtifactsCoordinates.isEmpty()) {
+ throw new MojoExecutionException(
+ "Build failed due to unused inherited dependencies in the dependency tree of the project.");
+ }
+
+ /* Writing the debloated version of the pom */
+ if (createPomDebloated) {
+ getLog().info("Starting debloating POM");
+
+ /* Add used transitive as direct dependencies */
+ try {
+ if (!usedTransitiveArtifacts.isEmpty()) {
+ getLog()
+ .info("Adding " + unusedTransitiveArtifactsCoordinates.size()
+ + " used transitive dependencies as direct dependencies.");
+ for (Artifact usedUndeclaredArtifact : usedTransitiveArtifacts) {
+ model.addDependency(createDependency(usedUndeclaredArtifact));
+ }
+ }
+ } catch (Exception e) {
+ throw new MojoExecutionException(e.getMessage(), e);
}
- // TODO Fix: The unused transitive dependencies induced by inherited dependencies should be considered as
- // unused inherited
- for (Artifact artifact : unusedTransitiveArtifacts) {
- String artifactGA = artifact.getGroupId() + ":" + artifact.getArtifactId();
- String artifactGAVS =
- artifactGA + ":" + artifact.toString().split(":")[3] + ":" + artifact.toString().split(":")[4];
- unusedTransitiveArtifactsCoordinates.add(artifactGAVS);
+ /* Remove unused direct dependencies */
+ try {
+ if (!unusedDirectArtifacts.isEmpty()) {
+ getLog().info("Removing " + unusedDirectArtifactsCoordinates.size()
+ + " unused direct dependencies.");
+ for (Artifact unusedDeclaredArtifact : unusedDirectArtifacts) {
+ for (Dependency dependency : model.getDependencies()) {
+ if (dependency.getGroupId().equals(unusedDeclaredArtifact.getGroupId())
+ && dependency.getArtifactId().equals(unusedDeclaredArtifact.getArtifactId())) {
+ model.removeDependency(dependency);
+ break;
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ throw new MojoExecutionException(e.getMessage(), e);
}
- /* Ignoring dependencies from the analysis */
- if (ignoreDependencies != null) {
- for (String ignoredDependency : ignoreDependencies) {
- // if the ignored dependency is an unused direct dependency then add it to the set of used direct
- // and remove it from the set of unused direct
- for (Iterator i = unusedDirectArtifactsCoordinates.iterator(); i.hasNext(); ) {
- String unusedDirectArtifact = i.next();
- if (ignoredDependency.equals(unusedDirectArtifact)) {
- usedDirectArtifactsCoordinates.add(unusedDirectArtifact);
- i.remove();
- break;
- }
- }
- // if the ignored dependency is an unused inherited dependency then add it to the set of used inherited
- // and remove it from the set of unused inherited
- for (Iterator j = unusedInheritedArtifactsCoordinates.iterator(); j.hasNext(); ) {
- String unusedInheritedArtifact = j.next();
- if (ignoredDependency.equals(unusedInheritedArtifact)) {
- usedInheritedArtifactsCoordinates.add(unusedInheritedArtifact);
- j.remove();
- break;
- }
- }
- // if the ignored dependency is an unused transitive dependency then add it to the set of used transitive
- // and remove it from the set of unused transitive
- for (Iterator j = unusedTransitiveArtifactsCoordinates.iterator(); j.hasNext(); ) {
- String unusedTransitiveArtifact = j.next();
- if (ignoredDependency.equals(unusedTransitiveArtifact)) {
- usedTransitiveArtifactsCoordinates.add(unusedTransitiveArtifact);
- j.remove();
- break;
- }
+ /* Exclude unused transitive dependencies */
+ try {
+ if (!unusedTransitiveArtifacts.isEmpty()) {
+ getLog().info(
+ "Excluding " + unusedTransitiveArtifacts.size()
+ + " unused transitive dependencies one-by-one.");
+ for (Dependency dependency : model.getDependencies()) {
+ for (Artifact artifact : unusedTransitiveArtifacts) {
+ if (isChildren(artifact, dependency)) {
+ getLog().info("Excluding " + artifact.toString() + " from dependency " + dependency
+ .toString());
+ Exclusion exclusion = new Exclusion();
+ exclusion.setGroupId(artifact.getGroupId());
+ exclusion.setArtifactId(artifact.getArtifactId());
+ dependency.addExclusion(exclusion);
+ }
}
- }
+ }
+ }
+ } catch (Exception e) {
+ throw new MojoExecutionException(e.getMessage(), e);
}
- /* Printing the results to the console */
- printString(SEPARATOR);
- printString(" D E P C L E A N A N A L Y S I S R E S U L T S");
- printString(SEPARATOR);
-
- printString("Used direct dependencies".toUpperCase() + " [" + usedDirectArtifactsCoordinates.size() + "]" + ": ");
- printDependencies(sizeOfDependencies, usedDirectArtifactsCoordinates);
-
- printString("Used inherited dependencies".toUpperCase() + " [" + usedInheritedArtifactsCoordinates.size() + "]" + ": ");
- printDependencies(sizeOfDependencies, usedInheritedArtifactsCoordinates);
-
- printString("Used transitive dependencies".toUpperCase() + " [" + usedTransitiveArtifactsCoordinates.size() +
- "]" + ": ");
- printDependencies(sizeOfDependencies, usedTransitiveArtifactsCoordinates);
-
- printString("Potentially unused direct dependencies".toUpperCase() + " [" + unusedDirectArtifactsCoordinates.size() + "]" + ": ");
- printDependencies(sizeOfDependencies, unusedDirectArtifactsCoordinates);
-
- printString("Potentially unused inherited dependencies".toUpperCase() + " [" + unusedInheritedArtifactsCoordinates.size() + "]" + ": ");
- printDependencies(sizeOfDependencies, unusedInheritedArtifactsCoordinates);
-
- printString("Potentially unused transitive dependencies".toUpperCase() + " [" + unusedTransitiveArtifactsCoordinates.size() + "]" + ": ");
- printDependencies(sizeOfDependencies, unusedTransitiveArtifactsCoordinates);
-
- if (!ignoreDependencies.isEmpty()) {
- printString(SEPARATOR);
- printString("Dependencies ignored in the analysis by the user" + " [" + ignoreDependencies.size() + "]" + ":" +
- " ");
- ignoreDependencies.stream().forEach(s -> printString("\t" + s));
+ /* Write the debloated pom file */
+ String pathToDebloatedPom =
+ project.getBasedir().getAbsolutePath() + File.separator + "pom-debloated.xml";
+ try {
+ Path path = Paths.get(pathToDebloatedPom);
+ writePom(path, model);
+ } catch (IOException e) {
+ throw new MojoExecutionException(e.getMessage(), e);
}
- /* Fail the build if there are unused direct dependencies */
- if (failIfUnusedDirect && !unusedDirectArtifactsCoordinates.isEmpty()) {
- throw new MojoExecutionException("Build failed due to unused direct dependencies in the dependency tree " +
- "of the project.");
- }
+ getLog().info("POM debloated successfully");
+ getLog().info("pom-debloated.xml file created in: " + pathToDebloatedPom);
+ }
- /* Fail the build if there are unused direct dependencies */
- if (failIfUnusedTransitive && !unusedTransitiveArtifactsCoordinates.isEmpty()) {
- throw new MojoExecutionException("Build failed due to unused transitive dependencies in the dependency " +
- "tree of the project.");
- }
- /* Fail the build if there are unused direct dependencies */
- if (failIfUnusedInherited && !unusedInheritedArtifactsCoordinates.isEmpty()) {
- throw new MojoExecutionException("Build failed due to unused inherited dependencies in the dependency " +
- "tree of the project.");
+ /* Writing the JSON file with the debloat results */
+ if (createResultJson) {
+ printString("Creating depclean-results.json, please wait...");
+ String pathToJsonFile =
+ project.getBasedir().getAbsolutePath() + File.separator + "depclean-results.json";
+ String treeFile = project.getBuild().getDirectory() + File.separator + "tree.txt";
+ /* Copy direct dependencies locally */
+ try {
+ MavenInvoker.runCommand("mvn dependency:tree -DoutputFile=" + treeFile + " -Dverbose=true");
+ } catch (IOException | InterruptedException e) {
+ getLog().error("Unable to generate dependency tree.");
+ // Restore interrupted state...
+ Thread.currentThread().interrupt();
+ return;
}
-
- /* Writing the debloated version of the pom */
- if (createPomDebloated) {
- getLog().info("Starting debloating POM");
-
- /* Add used transitive as direct dependencies */
- try {
- if (!usedTransitiveArtifacts.isEmpty()) {
- getLog().info("Adding " + unusedTransitiveArtifactsCoordinates.size() + " used transitive " +
- "dependencies as direct dependencies.");
- for (Artifact usedUndeclaredArtifact : usedTransitiveArtifacts) {
- model.addDependency(createDependency(usedUndeclaredArtifact));
- }
- }
- } catch (Exception e) {
- throw new MojoExecutionException(e.getMessage(), e);
- }
-
- /* Remove unused direct dependencies */
- try {
- if (!unusedDirectArtifacts.isEmpty()) {
- getLog().info("Removing " + unusedDirectArtifactsCoordinates.size() + " unused direct dependencies.");
- for (Artifact unusedDeclaredArtifact : unusedDirectArtifacts) {
- for (Dependency dependency : model.getDependencies()) {
- if (dependency.getGroupId().equals(unusedDeclaredArtifact.getGroupId()) &&
- dependency.getArtifactId().equals(unusedDeclaredArtifact.getArtifactId())) {
- model.removeDependency(dependency);
- break;
- }
- }
- }
- }
- } catch (Exception e) {
- throw new MojoExecutionException(e.getMessage(), e);
- }
-
- /* Exclude unused transitive dependencies */
- try {
- if (!unusedTransitiveArtifacts.isEmpty()) {
- getLog().info("Excluding " + unusedTransitiveArtifacts.size() + " unused transitive dependencies " +
- "one-by-one.");
- for (Dependency dependency : model.getDependencies()) {
- for (Artifact artifact : unusedTransitiveArtifacts) {
- if (isChildren(artifact, dependency)) {
- getLog().info("Excluding " + artifact.toString() + " from dependency " + dependency.toString());
- Exclusion exclusion = new Exclusion();
- exclusion.setGroupId(artifact.getGroupId());
- exclusion.setArtifactId(artifact.getArtifactId());
- dependency.addExclusion(exclusion);
- }
- }
- }
- }
- } catch (Exception e) {
- throw new MojoExecutionException(e.getMessage(), e);
- }
-
- /* Write the debloated pom file */
- String pathToDebloatedPom = project.getBasedir().getAbsolutePath() + File.separator + "pom-debloated.xml";
- try {
- Path path = Paths.get(pathToDebloatedPom);
- writePom(path, model);
- } catch (IOException e) {
- throw new MojoExecutionException(e.getMessage(), e);
- }
-
- getLog().info("POM debloated successfully");
- getLog().info("pom-debloated.xml file created in: " + pathToDebloatedPom);
+ File classUsageFile = new File(
+ project.getBasedir().getAbsolutePath() + File.separator + "class-usage.csv");
+ try {
+ FileUtils.write(classUsageFile, "OriginClass,TargetClass,Dependency\n",
+ Charset.defaultCharset());
+ } catch (IOException e) {
+ getLog().error("Error writing the CSV header.");
}
-
-
- /* Writing the JSON file with the debloat results */
- if (createResultJson) {
- printString("Creating depclean-results.json, please wait...");
- String pathToJsonFile = project.getBasedir().getAbsolutePath() + File.separator + "depclean-results.json";
- String treeFile = project.getBuild().getDirectory() + File.separator + "tree.txt";
- /* Copy direct dependencies locally */
- try {
- MavenInvoker.runCommand("mvn dependency:tree -DoutputFile=" + treeFile + " -Dverbose=true");
- } catch (IOException | InterruptedException e) {
- getLog().error("Unable to generate dependency tree.");
- // Restore interrupted state...
- Thread.currentThread().interrupt();
- return;
- }
- File classUsageFile = new File(project.getBasedir().getAbsolutePath() + File.separator + "class-usage.csv");
- try {
- FileUtils.write(classUsageFile, "OriginClass,TargetClass,Dependency\n", Charset.defaultCharset());
- } catch (IOException e) {
- getLog().error("Error writing the CSV header.");
- }
- ParsedDependencies parsedDependencies = new ParsedDependencies(
- treeFile,
- sizeOfDependencies,
- dependencyAnalyzer,
- usedDirectArtifactsCoordinates,
- usedInheritedArtifactsCoordinates,
- usedTransitiveArtifactsCoordinates,
- unusedDirectArtifactsCoordinates,
- unusedInheritedArtifactsCoordinates,
- unusedTransitiveArtifactsCoordinates,
- classUsageFile
- );
- try {
- FileUtils.write(new File(pathToJsonFile), parsedDependencies.parseTreeToJSON(), Charset.defaultCharset());
- getLog().info("depclean-results.json file created in: " + pathToJsonFile);
- } catch (ParseException | IOException e) {
- getLog().error("Unable to generate JSON file.");
- }
+ ParsedDependencies parsedDependencies = new ParsedDependencies(
+ treeFile,
+ sizeOfDependencies,
+ dependencyAnalyzer,
+ usedDirectArtifactsCoordinates,
+ usedInheritedArtifactsCoordinates,
+ usedTransitiveArtifactsCoordinates,
+ unusedDirectArtifactsCoordinates,
+ unusedInheritedArtifactsCoordinates,
+ unusedTransitiveArtifactsCoordinates,
+ classUsageFile
+ );
+ try {
+ FileUtils.write(new File(pathToJsonFile), parsedDependencies.parseTreeToJSON(),
+ Charset.defaultCharset());
+ getLog().info("depclean-results.json file created in: " + pathToJsonFile);
+ } catch (ParseException | IOException e) {
+ getLog().error("Unable to generate JSON file.");
}
- }
+ }
+ }
}
diff --git a/depclean-maven-plugin/src/main/java/se/kth/depclean/util/FileUtils.java b/depclean-maven-plugin/src/main/java/se/kth/depclean/util/FileUtils.java
index b0d2dd56..d43e4a91 100644
--- a/depclean-maven-plugin/src/main/java/se/kth/depclean/util/FileUtils.java
+++ b/depclean-maven-plugin/src/main/java/se/kth/depclean/util/FileUtils.java
@@ -17,77 +17,76 @@
package se.kth.depclean.util;
-import lombok.extern.slf4j.Slf4j;
-
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
+import lombok.extern.slf4j.Slf4j;
@Slf4j
public final class FileUtils {
- private FileUtils() {
- }
+ private FileUtils() {
+ }
- /**
- * Deletes a directory recursively.
- *
- * @param directory The directory to be deleted.
- * @throws IOException In case of IO issues.
- */
- public static void deleteDirectory(final File directory) throws IOException {
- if (!directory.exists()) {
- return;
- }
- if (!isSymlink(directory)) {
- cleanDirectory(directory);
- }
- Files.delete(directory.toPath());
+ /**
+ * Deletes a directory recursively.
+ *
+ * @param directory The directory to be deleted.
+ * @throws IOException In case of IO issues.
+ */
+ public static void deleteDirectory(final File directory) throws IOException {
+ if (!directory.exists()) {
+ return;
}
+ if (!isSymlink(directory)) {
+ cleanDirectory(directory);
+ }
+ Files.delete(directory.toPath());
+ }
- private static boolean isSymlink(final File file) {
- if (file == null) {
- throw new NullPointerException("File must be not null");
- }
- return Files.isSymbolicLink(file.toPath());
+ private static boolean isSymlink(final File file) {
+ if (file == null) {
+ throw new NullPointerException("File must be not null");
}
+ return Files.isSymbolicLink(file.toPath());
+ }
- private static void cleanDirectory(final File directory) throws IOException {
- final File[] files = verifiedListFiles(directory);
- IOException exception = null;
- for (final File file : files) {
- try {
- forceDelete(file);
- } catch (final IOException ioe) {
- exception = ioe;
- }
- }
- if (null != exception) {
- throw exception;
- }
+ private static void cleanDirectory(final File directory) throws IOException {
+ final File[] files = verifiedListFiles(directory);
+ IOException exception = null;
+ for (final File file : files) {
+ try {
+ forceDelete(file);
+ } catch (final IOException ioe) {
+ exception = ioe;
+ }
+ }
+ if (null != exception) {
+ throw exception;
}
+ }
- private static File[] verifiedListFiles(final File directory) throws IOException {
- if (!directory.exists()) {
- final String message = directory + " does not exist";
- throw new IllegalArgumentException(message);
- }
- if (!directory.isDirectory()) {
- final String message = directory + " is not a directory";
- throw new IllegalArgumentException(message);
- }
- final File[] files = directory.listFiles();
- if (files == null) { // null if security restricted
- throw new IOException("Failed to list contents of " + directory);
- }
- return files;
+ private static File[] verifiedListFiles(final File directory) throws IOException {
+ if (!directory.exists()) {
+ final String message = directory + " does not exist";
+ throw new IllegalArgumentException(message);
+ }
+ if (!directory.isDirectory()) {
+ final String message = directory + " is not a directory";
+ throw new IllegalArgumentException(message);
+ }
+ final File[] files = directory.listFiles();
+ if (files == null) { // null if security restricted
+ throw new IOException("Failed to list contents of " + directory);
}
+ return files;
+ }
- private static void forceDelete(final File file) throws IOException {
- if (file.isDirectory()) {
- deleteDirectory(file);
- } else {
- Files.delete(file.toPath());
- }
+ private static void forceDelete(final File file) throws IOException {
+ if (file.isDirectory()) {
+ deleteDirectory(file);
+ } else {
+ Files.delete(file.toPath());
}
+ }
}
diff --git a/depclean-maven-plugin/src/main/java/se/kth/depclean/util/JarUtils.java b/depclean-maven-plugin/src/main/java/se/kth/depclean/util/JarUtils.java
index a61f56d8..8e36a149 100644
--- a/depclean-maven-plugin/src/main/java/se/kth/depclean/util/JarUtils.java
+++ b/depclean-maven-plugin/src/main/java/se/kth/depclean/util/JarUtils.java
@@ -17,8 +17,6 @@
package se.kth.depclean.util;
-import lombok.extern.slf4j.Slf4j;
-
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -27,80 +25,82 @@
import java.util.Objects;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
+import lombok.extern.slf4j.Slf4j;
@Slf4j
public final class JarUtils {
- /**
- * Size of the buffer to read/write data.
- */
- private static final int BUFFER_SIZE = 16384;
+ /**
+ * Size of the buffer to read/write data.
+ */
+ private static final int BUFFER_SIZE = 16384;
- private JarUtils() {
- }
+ private JarUtils() {
+ }
- /**
- * Decompress all jar files located in a given directory.
- *
- * @param outputDirectory The directory path to put the decompressed files.
- */
- public static void decompressJars(final String outputDirectory) {
- File files = new File(outputDirectory);
- for (File f : Objects.requireNonNull(files.listFiles())) {
- if (f.getName().endsWith(".jar")) {
- try {
- JarUtils.decompressJarFile(outputDirectory, f.getAbsolutePath());
- // delete the original dependency jar file
- org.apache.commons.io.FileUtils.forceDelete(f);
- } catch (IOException e) {
- log.warn("Problem decompressing jar file: " + f.getAbsolutePath());
- }
- }
+ /**
+ * Decompress all jar files located in a given directory.
+ *
+ * @param outputDirectory The directory path to put the decompressed files.
+ */
+ public static void decompressJars(final String outputDirectory) {
+ File files = new File(outputDirectory);
+ for (File f : Objects.requireNonNull(files.listFiles())) {
+ if (f.getName().endsWith(".jar")) {
+ try {
+ JarUtils.decompressJarFile(outputDirectory, f.getAbsolutePath());
+ // delete the original dependency jar file
+ org.apache.commons.io.FileUtils.forceDelete(f);
+ } catch (IOException e) {
+ log.warn("Problem decompressing jar file: " + f.getAbsolutePath());
}
+ }
}
+ }
- /**
- * Decompress a jar file in a path to a directory (will be created if it doesn't exists).
- *
- * @param destDirectory The destine directory.
- * @param jarFilePath The path to the Jar file to be decompressed.
- * @throws IOException In case of IO issues.
- */
- private static void decompressJarFile(String destDirectory, String jarFilePath) throws IOException {
- File destDir = new File(destDirectory);
- if (!destDir.exists()) {
- destDir.mkdir();
- }
- try (JarInputStream jarIn = new JarInputStream(new FileInputStream(jarFilePath))) {
- JarEntry entry = jarIn.getNextJarEntry();
- // iterates over all the entries in the jar file
- while (entry != null) {
- String filePath = destDirectory + File.separator + entry.getName();
- if (!entry.isDirectory()) {
- new File(filePath).getParentFile().mkdirs(); //NOSONAR Triggers a false warning of path traversal attack
- // if the entry is a file, extracts it
- extractFile(jarIn, filePath);
- }
- jarIn.closeEntry();
- entry = jarIn.getNextJarEntry();
- }
+ /**
+ * Decompress a jar file in a path to a directory (will be created if it doesn't exists).
+ *
+ * @param destDirectory The destine directory.
+ * @param jarFilePath The path to the Jar file to be decompressed.
+ * @throws IOException In case of IO issues.
+ */
+ private static void decompressJarFile(String destDirectory, String jarFilePath) throws IOException {
+ File destDir = new File(destDirectory);
+ if (!destDir.exists()) {
+ destDir.mkdir();
+ }
+ try (JarInputStream jarIn = new JarInputStream(new FileInputStream(jarFilePath))) {
+ JarEntry entry = jarIn.getNextJarEntry();
+ // iterates over all the entries in the jar file
+ while (entry != null) {
+ String filePath = destDirectory + File.separator + entry.getName();
+ if (!entry.isDirectory()) {
+ new File(filePath).getParentFile().mkdirs(); //NOSONAR Triggers a false warning of path traversal attack
+ // if the entry is a file, extracts it
+ extractFile(jarIn, filePath);
}
+ jarIn.closeEntry();
+ entry = jarIn.getNextJarEntry();
+ }
}
+ }
- /**
- * Extracts an entry file.
- *
- * @param jarIn The jar file to be extracted.
- * @param filePath Path to the file.
- * @throws IOException In case of IO issues.
- */
- private static void extractFile(final JarInputStream jarIn, final String filePath) throws IOException {
- try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath))) { //NOSONAR Triggers a false warning of path traversal attack
- byte[] bytesIn = new byte[BUFFER_SIZE];
- int read;
- while ((read = jarIn.read(bytesIn)) != -1) {
- bos.write(bytesIn, 0, read);
- }
- }
+ /**
+ * Extracts an entry file.
+ *
+ * @param jarIn The jar file to be extracted.
+ * @param filePath Path to the file.
+ * @throws IOException In case of IO issues.
+ */
+ private static void extractFile(final JarInputStream jarIn, final String filePath) throws IOException {
+ try (BufferedOutputStream bos = new BufferedOutputStream(
+ new FileOutputStream(filePath))) { //NOSONAR Triggers a false warning of path traversal attack
+ byte[] bytesIn = new byte[BUFFER_SIZE];
+ int read;
+ while ((read = jarIn.read(bytesIn)) != -1) {
+ bos.write(bytesIn, 0, read);
+ }
}
+ }
}
diff --git a/depclean-maven-plugin/src/main/java/se/kth/depclean/util/MavenInvoker.java b/depclean-maven-plugin/src/main/java/se/kth/depclean/util/MavenInvoker.java
index 22508bf8..81536635 100644
--- a/depclean-maven-plugin/src/main/java/se/kth/depclean/util/MavenInvoker.java
+++ b/depclean-maven-plugin/src/main/java/se/kth/depclean/util/MavenInvoker.java
@@ -17,14 +17,6 @@
package se.kth.depclean.util;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.maven.shared.invoker.DefaultInvocationRequest;
-import org.apache.maven.shared.invoker.DefaultInvoker;
-import org.apache.maven.shared.invoker.InvocationRequest;
-import org.apache.maven.shared.invoker.InvocationResult;
-import org.apache.maven.shared.invoker.Invoker;
-import org.apache.maven.shared.invoker.MavenInvocationException;
-
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
@@ -32,78 +24,86 @@
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.maven.shared.invoker.DefaultInvocationRequest;
+import org.apache.maven.shared.invoker.DefaultInvoker;
+import org.apache.maven.shared.invoker.InvocationRequest;
+import org.apache.maven.shared.invoker.InvocationResult;
+import org.apache.maven.shared.invoker.Invoker;
+import org.apache.maven.shared.invoker.MavenInvocationException;
@Slf4j
public final class MavenInvoker {
- private MavenInvoker() {
- }
+ private MavenInvoker() {
+ }
- /**
- * Creates a native process to execute a custom command. This method is used to invoke maven plugins directly.
- *
- * @param cmd The command to be executed.
- * @return The console output.
- * @throws IOException In case of IO issues.
- * @throws InterruptedException In case of IO issues.
- */
- public static String[] runCommand(String cmd) throws IOException, InterruptedException {
- String os = System.getProperty("os.name").toLowerCase();
- Process process;
- ArrayList list;
- if (isUnix(os)) {
- list = new ArrayList<>();
- process = Runtime.getRuntime().exec(cmd);
- InputStream inputStream = process.getInputStream();
- BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
- return outputToConsole(process, list, br);
- } else if (isWindows(os)) {
- list = new ArrayList<>();
- process = Runtime.getRuntime().exec("cmd /C " + cmd);
- BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
- return outputToConsole(process, list, br);
- }
- return new String[0]; // return an empty array otherwise
- }
+ /**
+ * Creates a native process to execute a custom command. This method is used to invoke maven plugins directly.
+ *
+ * @param cmd The command to be executed.
+ * @return The console output.
+ * @throws IOException In case of IO issues.
+ * @throws InterruptedException In case of IO issues.
+ */
+ public static String[] runCommand(String cmd) throws IOException, InterruptedException {
+ String os = System.getProperty("os.name").toLowerCase();
+ Process process;
+ ArrayList list;
+ if (isUnix(os)) {
+ list = new ArrayList<>();
+ process = Runtime.getRuntime().exec(cmd);
+ InputStream inputStream = process.getInputStream();
+ BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
+ return outputToConsole(process, list, br);
+ } else if (isWindows(os)) {
+ list = new ArrayList<>();
+ process = Runtime.getRuntime().exec("cmd /C " + cmd);
+ BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
+ return outputToConsole(process, list, br);
+ }
+ return new String[0]; // return an empty array otherwise
+ }
- /**
- * Print the output of the command to the standard output.
- */
- private static String[] outputToConsole(Process process, ArrayList list, BufferedReader br) throws IOException, InterruptedException {
- String s;
- while ((s = br.readLine()) != null) {
- list.add(s);
- }
- process.waitFor();
- br.close();
- return list.toArray(new String[0]);
- }
+ /**
+ * Print the output of the command to the standard output.
+ */
+ private static String[] outputToConsole(Process process, ArrayList list, BufferedReader br)
+ throws IOException, InterruptedException {
+ String s;
+ while ((s = br.readLine()) != null) {
+ list.add(s);
+ }
+ process.waitFor();
+ br.close();
+ return list.toArray(new String[0]);
+ }
- /**
- * This method invokes Maven to execute a given goal programmatically instead of running a command directly as
- * in {@link #runCommand(String)}.
- *
- * @param mvnHome Location of maven installation.
- * @param pomPath Path to the pom of the project.
- * @param mvnGoal The maven goal to execute.
- * @return The exit code from the Maven invocation.
- * @throws MavenInvocationException In case of any issue invoking maven.
- */
- public static int invokeMaven(String mvnHome, String pomPath, String mvnGoal) throws MavenInvocationException {
- InvocationRequest request = new DefaultInvocationRequest();
- request.setPomFile(new File(pomPath));
- request.setGoals(Collections.singletonList(mvnGoal));
- Invoker invoker = new DefaultInvoker();
- invoker.setMavenHome(new File(mvnHome));
- InvocationResult result = invoker.execute(request);
- return result.getExitCode();
- }
+ /**
+ * This method invokes Maven to execute a given goal programmatically instead of running a command directly as in
+ * {@link #runCommand(String)}.
+ *
+ * @param mvnHome Location of maven installation.
+ * @param pomPath Path to the pom of the project.
+ * @param mvnGoal The maven goal to execute.
+ * @return The exit code from the Maven invocation.
+ * @throws MavenInvocationException In case of any issue invoking maven.
+ */
+ public static int invokeMaven(String mvnHome, String pomPath, String mvnGoal) throws MavenInvocationException {
+ InvocationRequest request = new DefaultInvocationRequest();
+ request.setPomFile(new File(pomPath));
+ request.setGoals(Collections.singletonList(mvnGoal));
+ Invoker invoker = new DefaultInvoker();
+ invoker.setMavenHome(new File(mvnHome));
+ InvocationResult result = invoker.execute(request);
+ return result.getExitCode();
+ }
- private static boolean isUnix(String os) {
- return (os.contains("nix") || os.contains("nux") || os.contains("mac os"));
- }
+ private static boolean isUnix(String os) {
+ return (os.contains("nix") || os.contains("nux") || os.contains("mac os"));
+ }
- private static boolean isWindows(String os) {
- return (os.contains("win"));
- }
+ private static boolean isWindows(String os) {
+ return (os.contains("win"));
+ }
}
diff --git a/depclean-maven-plugin/src/main/java/se/kth/depclean/util/json/NodeAdapter.java b/depclean-maven-plugin/src/main/java/se/kth/depclean/util/json/NodeAdapter.java
index 0065a4b8..2833040b 100644
--- a/depclean-maven-plugin/src/main/java/se/kth/depclean/util/json/NodeAdapter.java
+++ b/depclean-maven-plugin/src/main/java/se/kth/depclean/util/json/NodeAdapter.java
@@ -4,155 +4,156 @@
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import fr.dutra.tools.maven.deptree.core.Node;
-import org.apache.commons.io.FileUtils;
-import se.kth.depclean.core.analysis.DefaultProjectDependencyAnalyzer;
-import se.kth.depclean.core.analysis.graph.DefaultCallGraph;
-
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.Set;
+import org.apache.commons.io.FileUtils;
+import se.kth.depclean.core.analysis.DefaultProjectDependencyAnalyzer;
+import se.kth.depclean.core.analysis.graph.DefaultCallGraph;
public class NodeAdapter extends TypeAdapter {
- private final Set usedDirectArtifactsCoordinates;
- private final Set usedInheritedArtifactsCoordinates;
- private final Set usedTransitiveArtifactsCoordinates;
- private final Set unusedDirectArtifactsCoordinates;
- private final Set unusedInheritedArtifactsCoordinates;
- private final Set unusedTransitiveArtifactsCoordinates;
- private final DefaultProjectDependencyAnalyzer dependencyAnalyzer;
- private final File classUsageFile;
-
- private final Map sizeOfDependencies;
-
- public NodeAdapter(Set usedDirectArtifactsCoordinates,
- Set usedInheritedArtifactsCoordinates,
- Set usedTransitiveArtifactsCoordinates,
- Set unusedDirectArtifactsCoordinates,
- Set unusedInheritedArtifactsCoordinates,
- Set unusedTransitiveArtifactsCoordinates,
- Map sizeOfDependencies,
- DefaultProjectDependencyAnalyzer dependencyAnalyzer,
- File classUsageFile) {
- this.usedDirectArtifactsCoordinates = usedDirectArtifactsCoordinates;
- this.usedInheritedArtifactsCoordinates = usedInheritedArtifactsCoordinates;
- this.usedTransitiveArtifactsCoordinates = usedTransitiveArtifactsCoordinates;
- this.unusedDirectArtifactsCoordinates = unusedDirectArtifactsCoordinates;
- this.unusedInheritedArtifactsCoordinates = unusedInheritedArtifactsCoordinates;
- this.unusedTransitiveArtifactsCoordinates = unusedTransitiveArtifactsCoordinates;
- this.sizeOfDependencies = sizeOfDependencies;
- this.dependencyAnalyzer = dependencyAnalyzer;
- this.classUsageFile = classUsageFile;
- }
-
- @Override
- public void write(JsonWriter jsonWriter, Node node) throws IOException {
- String coordinates =
- node.getGroupId() + ":" + node.getArtifactId() + ":" + node.getVersion() + ":" + node.getScope();
- String canonical =
- node.getGroupId() + ":" + node.getArtifactId() + ":" + node.getPackaging() + ":" + node.getVersion() +
- ":" + node.getScope();
- String dependencyJar = node.getArtifactId() + "-" + node.getVersion() + ".jar";
-
- // Write to the class-usage.csv file
- DefaultCallGraph defaultCallGraph = new DefaultCallGraph();
- for (Map.Entry> usagePerClassMap : defaultCallGraph.getUsagesPerClass().entrySet()) {
- String key = usagePerClassMap.getKey();
- Set value = usagePerClassMap.getValue();
- for (String s : value) {
- if (dependencyAnalyzer.getArtifactClassesMap().containsKey(canonical) && dependencyAnalyzer.getArtifactClassesMap().get(canonical).getAllTypes().contains(s)) {
- // System.out.println(key + " uses " + s + " from " + canonical);
- String triplet = key + "," + s + "," + canonical + "\n";
- FileUtils.write(classUsageFile, triplet, Charset.defaultCharset(), true);
- }
- }
+ private final Set usedDirectArtifactsCoordinates;
+ private final Set usedInheritedArtifactsCoordinates;
+ private final Set usedTransitiveArtifactsCoordinates;
+ private final Set unusedDirectArtifactsCoordinates;
+ private final Set unusedInheritedArtifactsCoordinates;
+ private final Set unusedTransitiveArtifactsCoordinates;
+ private final DefaultProjectDependencyAnalyzer dependencyAnalyzer;
+ private final File classUsageFile;
+
+ private final Map sizeOfDependencies;
+
+ public NodeAdapter(Set usedDirectArtifactsCoordinates,
+ Set usedInheritedArtifactsCoordinates,
+ Set usedTransitiveArtifactsCoordinates,
+ Set unusedDirectArtifactsCoordinates,
+ Set unusedInheritedArtifactsCoordinates,
+ Set unusedTransitiveArtifactsCoordinates,
+ Map sizeOfDependencies,
+ DefaultProjectDependencyAnalyzer dependencyAnalyzer,
+ File classUsageFile) {
+ this.usedDirectArtifactsCoordinates = usedDirectArtifactsCoordinates;
+ this.usedInheritedArtifactsCoordinates = usedInheritedArtifactsCoordinates;
+ this.usedTransitiveArtifactsCoordinates = usedTransitiveArtifactsCoordinates;
+ this.unusedDirectArtifactsCoordinates = unusedDirectArtifactsCoordinates;
+ this.unusedInheritedArtifactsCoordinates = unusedInheritedArtifactsCoordinates;
+ this.unusedTransitiveArtifactsCoordinates = unusedTransitiveArtifactsCoordinates;
+ this.sizeOfDependencies = sizeOfDependencies;
+ this.dependencyAnalyzer = dependencyAnalyzer;
+ this.classUsageFile = classUsageFile;
+ }
+
+ @Override
+ public void write(JsonWriter jsonWriter, Node node) throws IOException {
+ String coordinates =
+ node.getGroupId() + ":" + node.getArtifactId() + ":" + node.getVersion() + ":" + node.getScope();
+ String canonical =
+ node.getGroupId() + ":" + node.getArtifactId() + ":" + node.getPackaging() + ":" + node.getVersion()
+ + ":" + node.getScope();
+ String dependencyJar = node.getArtifactId() + "-" + node.getVersion() + ".jar";
+
+ // Write to the class-usage.csv file
+ DefaultCallGraph defaultCallGraph = new DefaultCallGraph();
+ for (Map.Entry> usagePerClassMap : defaultCallGraph.getUsagesPerClass().entrySet()) {
+ String key = usagePerClassMap.getKey();
+ Set value = usagePerClassMap.getValue();
+ for (String s : value) {
+ if (dependencyAnalyzer.getArtifactClassesMap().containsKey(canonical) && dependencyAnalyzer
+ .getArtifactClassesMap().get(canonical).getAllTypes().contains(s)) {
+ // System.out.println(key + " uses " + s + " from " + canonical);
+ String triplet = key + "," + s + "," + canonical + "\n";
+ FileUtils.write(classUsageFile, triplet, Charset.defaultCharset(), true);
+ }
}
-
- JsonWriter localWriter = jsonWriter.beginObject()
- .name("id")
- .value(canonical)
-
- .name("coordinates")
- .value(node.getGroupId() + ":" + node.getArtifactId() + ":" + node.getVersion())
-
- .name("groupId")
- .value(node.getGroupId())
-
- .name("artifactId")
- .value(node.getArtifactId())
-
- .name("version")
- .value(node.getVersion())
-
- .name("scope")
- .value(node.getScope())
-
- .name("packaging")
- .value(node.getPackaging())
-
- .name("omitted")
- .value(node.isOmitted())
-
- .name("classifier")
- .value(node.getClassifier())
-
- .name("size")
- .value(sizeOfDependencies.get(dependencyJar))
-
- .name("type")
- .value((usedDirectArtifactsCoordinates.contains(coordinates) || unusedDirectArtifactsCoordinates.contains(coordinates)) ? "direct" :
- (usedInheritedArtifactsCoordinates.contains(coordinates) || unusedInheritedArtifactsCoordinates.contains(coordinates)) ? "inherited" :
- (usedTransitiveArtifactsCoordinates.contains(coordinates) || unusedTransitiveArtifactsCoordinates.contains(coordinates)) ? "transitive" :
- "unknown")
- .name("status")
- .value((usedDirectArtifactsCoordinates.contains(coordinates) || usedInheritedArtifactsCoordinates.contains(coordinates) || usedTransitiveArtifactsCoordinates.contains(coordinates)) ?
- "used" :
- (unusedDirectArtifactsCoordinates.contains(coordinates) || unusedInheritedArtifactsCoordinates.contains(coordinates) || unusedTransitiveArtifactsCoordinates.contains(coordinates)) ?
- "bloated" :
- "unknown")
-
- .name("parent")
- .value(node.getParent() != null ?
- node.getParent().getArtifactCanonicalForm() :
- "unknown");
-
- JsonWriter allTypes = localWriter.name("allTypes").beginArray();
- if (dependencyAnalyzer.getArtifactClassesMap().containsKey(canonical)) {
- for (String allType : dependencyAnalyzer.getArtifactClassesMap().get(canonical).getAllTypes()) {
- allTypes.value(allType);
- }
+ }
+
+ JsonWriter localWriter = jsonWriter.beginObject()
+ .name("id")
+ .value(canonical)
+
+ .name("coordinates")
+ .value(node.getGroupId() + ":" + node.getArtifactId() + ":" + node.getVersion())
+
+ .name("groupId")
+ .value(node.getGroupId())
+
+ .name("artifactId")
+ .value(node.getArtifactId())
+
+ .name("version")
+ .value(node.getVersion())
+
+ .name("scope")
+ .value(node.getScope())
+
+ .name("packaging")
+ .value(node.getPackaging())
+
+ .name("omitted")
+ .value(node.isOmitted())
+
+ .name("classifier")
+ .value(node.getClassifier())
+
+ .name("size")
+ .value(sizeOfDependencies.get(dependencyJar))
+
+ .name("type")
+ .value((usedDirectArtifactsCoordinates.contains(coordinates) || unusedDirectArtifactsCoordinates
+ .contains(coordinates)) ? "direct" :
+ (usedInheritedArtifactsCoordinates.contains(coordinates) || unusedInheritedArtifactsCoordinates
+ .contains(coordinates)) ? "inherited" :
+ (usedTransitiveArtifactsCoordinates.contains(coordinates) || unusedTransitiveArtifactsCoordinates
+ .contains(coordinates)) ? "transitive" : "unknown")
+ .name("status")
+ .value((usedDirectArtifactsCoordinates.contains(coordinates) || usedInheritedArtifactsCoordinates
+ .contains(coordinates) || usedTransitiveArtifactsCoordinates.contains(coordinates))
+ ? "used" :
+ (unusedDirectArtifactsCoordinates.contains(coordinates) || unusedInheritedArtifactsCoordinates
+ .contains(coordinates) || unusedTransitiveArtifactsCoordinates.contains(coordinates))
+ ? "bloated" : "unknown")
+
+ .name("parent")
+ .value(node.getParent() != null
+ ? node.getParent().getArtifactCanonicalForm() : "unknown");
+
+ JsonWriter allTypes = localWriter.name("allTypes").beginArray();
+ if (dependencyAnalyzer.getArtifactClassesMap().containsKey(canonical)) {
+ for (String allType : dependencyAnalyzer.getArtifactClassesMap().get(canonical).getAllTypes()) {
+ allTypes.value(allType);
}
- allTypes.endArray();
+ }
+ allTypes.endArray();
- JsonWriter usedTypes = localWriter.name("usedTypes").beginArray();
- if (dependencyAnalyzer.getArtifactClassesMap().containsKey(canonical)) {
- for (String usedType : dependencyAnalyzer.getArtifactClassesMap().get(canonical).getUsedTypes()) {
- usedTypes.value(usedType);
- }
- }
- usedTypes.endArray();
-
- localWriter.name("usageRatio")
- .value(dependencyAnalyzer.getArtifactClassesMap().containsKey(canonical) ?
- dependencyAnalyzer.getArtifactClassesMap().get(canonical).getAllTypes().isEmpty() ?
- 0 : // handle division by zero
- ((double) dependencyAnalyzer.getArtifactClassesMap().get(canonical).getUsedTypes().size() /
- dependencyAnalyzer.getArtifactClassesMap().get(canonical).getAllTypes().size()) :
- -1)
- .name("children")
- .beginArray();
-
- for (Node c : node.getChildNodes()) {
- this.write(jsonWriter, c);
+ JsonWriter usedTypes = localWriter.name("usedTypes").beginArray();
+ if (dependencyAnalyzer.getArtifactClassesMap().containsKey(canonical)) {
+ for (String usedType : dependencyAnalyzer.getArtifactClassesMap().get(canonical).getUsedTypes()) {
+ usedTypes.value(usedType);
}
- jsonWriter.endArray()
- .endObject();
- }
-
- @Override
- public Node read(JsonReader jsonReader) {
- throw new UnsupportedOperationException();
- }
+ }
+ usedTypes.endArray();
+
+ localWriter.name("usageRatio")
+ .value(dependencyAnalyzer.getArtifactClassesMap().containsKey(canonical)
+ ? dependencyAnalyzer.getArtifactClassesMap().get(canonical).getAllTypes().isEmpty()
+ ? 0 : // handle division by zero
+ ((double) dependencyAnalyzer.getArtifactClassesMap().get(canonical).getUsedTypes().size()
+ / dependencyAnalyzer.getArtifactClassesMap().get(canonical).getAllTypes().size()) : -1)
+ .name("children")
+ .beginArray();
+
+ for (Node c : node.getChildNodes()) {
+ this.write(jsonWriter, c);
+ }
+ jsonWriter.endArray()
+ .endObject();
+ }
+
+ @Override
+ public Node read(JsonReader jsonReader) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/depclean-maven-plugin/src/main/java/se/kth/depclean/util/json/ParsedDependencies.java b/depclean-maven-plugin/src/main/java/se/kth/depclean/util/json/ParsedDependencies.java
index 146b0364..cd88aa68 100644
--- a/depclean-maven-plugin/src/main/java/se/kth/depclean/util/json/ParsedDependencies.java
+++ b/depclean-maven-plugin/src/main/java/se/kth/depclean/util/json/ParsedDependencies.java
@@ -6,9 +6,6 @@
import fr.dutra.tools.maven.deptree.core.Node;
import fr.dutra.tools.maven.deptree.core.ParseException;
import fr.dutra.tools.maven.deptree.core.Parser;
-import lombok.extern.slf4j.Slf4j;
-import se.kth.depclean.core.analysis.DefaultProjectDependencyAnalyzer;
-
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
@@ -18,67 +15,69 @@
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Set;
+import lombok.extern.slf4j.Slf4j;
+import se.kth.depclean.core.analysis.DefaultProjectDependencyAnalyzer;
@Slf4j
public class ParsedDependencies {
- private final String treeTextFilePath;
- private final DefaultProjectDependencyAnalyzer dependencyAnalyzer;
- private final Set usedDirectArtifactsCoordinates;
- private final Set usedInheritedArtifactsCoordinates;
- private final Set usedTransitiveArtifactsCoordinates;
- private final Set unusedDirectArtifactsCoordinates;
- private final Set