diff --git a/pom.xml b/pom.xml index 0b8f74b1..e34e1404 100644 --- a/pom.xml +++ b/pom.xml @@ -232,6 +232,12 @@ SOFTWARE. + + org.jetbrains + annotations + RELEASE + compile + diff --git a/src/main/java/org/jpeek/Metrics.java b/src/main/java/org/jpeek/Metrics.java index 422d7019..e9a18ea1 100644 --- a/src/main/java/org/jpeek/Metrics.java +++ b/src/main/java/org/jpeek/Metrics.java @@ -110,6 +110,13 @@ public enum Metrics { */ CCM(false, null, null), + + /** + * Class Connection Metric (CCM) metric. + * Measuring Class Cohesion in Object-Oriented Systems version 3 + */ + CCM_V3(false, null, null), + /** * Maximal Weighted Entropy metric. Modeling class cohesion as mixtures of latent topics */ diff --git a/src/main/java/org/jpeek/graph/XmlGraph.java b/src/main/java/org/jpeek/graph/XmlGraph.java index c912e811..c065adc0 100644 --- a/src/main/java/org/jpeek/graph/XmlGraph.java +++ b/src/main/java/org/jpeek/graph/XmlGraph.java @@ -24,8 +24,9 @@ package org.jpeek.graph; import com.jcabi.xml.XML; -import java.util.List; -import java.util.Map; + +import java.util.*; + import org.cactoos.list.ListOf; import org.cactoos.map.MapOf; import org.cactoos.scalar.Sticky; @@ -74,47 +75,32 @@ public List nodes() { * @param cname Class in the skeleton this graph is for * @return List of nodes */ - private static List build(final Skeleton skeleton, final String pname, - final String cname) { - final Map byxml = new MapOf<>( - method -> method, - method -> new Node.Simple( - new XmlMethodSignature( - skeleton.xml() - .nodes( - new FormattedText( - "//package[@id='%s']", pname - ).toString() - ).get(0) - .nodes( - new FormattedText( - "//class[@id='%s']", cname - ).toString() - ).get(0), - method - ).asString() - ), - skeleton.xml().nodes( - "//methods/method[@ctor='false' and @abstract='false']" - ) - ); - final Map byname = new MapOf<>( - Node::name, - node -> node, - byxml.values() - ); - for (final Map.Entry entry : byxml.entrySet()) { - final List calls = entry.getKey().nodes("ops/op[@code='call']"); - final Node caller = entry.getValue(); - for (final XML call : calls) { - final String name = new XmlMethodCall(call).toString(); - if (byname.containsKey(name)) { - final Node callee = byname.get(name); + private static List build(final Skeleton skeleton, final String pname, final String cname) throws Exception { + final Map byxml = new HashMap<>(); + final Set visitedNames = new HashSet<>(); + for (XML methodXml : skeleton.xml().nodes("//methods/method[@ctor='false' and @abstract='false']")) { + String methodName = new XmlMethodSignature(skeleton.xml().nodes(new FormattedText("//package[@id='%s']", pname).toString()) + .get(0) + .nodes(new FormattedText("//class[@id='%s']", cname).toString()) + .get(0), methodXml).asString(); + if (!visitedNames.contains(methodName)) { + Node.Simple node = new Node.Simple(methodName); + byxml.put(methodXml, node); + visitedNames.add(methodName); + } + } + for (Map.Entry entry : byxml.entrySet()) { + XML methodXml = entry.getKey(); + Node caller = entry.getValue(); + for (XML call : methodXml.nodes("ops/op[@code='call']")) { + String calleeName = new XmlMethodCall(call).toString(); + if (visitedNames.contains(calleeName)) { + Node callee = byxml.get(call); // assuming they exist in byxml caller.connections().add(callee); callee.connections().add(caller); } } } - return new ListOf<>(byxml.values()); + return new ArrayList<>(byxml.values()); } } diff --git a/src/main/java/org/jpeek/skeleton/XmlClass.java b/src/main/java/org/jpeek/skeleton/XmlClass.java index 93372ccf..9f1418c0 100644 --- a/src/main/java/org/jpeek/skeleton/XmlClass.java +++ b/src/main/java/org/jpeek/skeleton/XmlClass.java @@ -27,10 +27,12 @@ import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; + import javassist.CannotCompileException; import javassist.CtClass; import org.cactoos.iterable.Joined; import org.cactoos.iterable.Mapped; +import org.jetbrains.annotations.NotNull; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; @@ -44,9 +46,9 @@ * *

There is no thread-safety guarantee.

* + * @checkstyle ParameterNumberCheck (500 lines) * @see A packages suite for object oriented design * @since 0.27 - * @checkstyle ParameterNumberCheck (500 lines) */ final class XmlClass extends ClassVisitor implements Iterable { @@ -67,6 +69,7 @@ final class XmlClass extends ClassVisitor implements Iterable { /** * Ctor. + * * @param src The source */ XmlClass(final CtClass src) { @@ -77,6 +80,7 @@ final class XmlClass extends ClassVisitor implements Iterable { } @Override + @NotNull public Iterator iterator() { final ClassReader reader; try { @@ -87,42 +91,42 @@ public Iterator iterator() { this.attrs.add("attributes"); reader.accept(this, 0); return new Directives() - .append(this.attrs) - .up() - .add("methods") - .append( - new Joined<>( - new Mapped<>( - dirs -> new Directives().append(dirs).up(), - this.methods - ) + .append(this.attrs) + .up() + .add("methods") + .append( + new Joined<>( + new Mapped<>( + dirs -> new Directives().append(dirs).up(), + this.methods + ) + ) ) - ) - .up() - .iterator(); + .up() + .iterator(); } @Override public FieldVisitor visitField(final int access, - final String name, final String desc, - final String signature, final Object value) { + final String name, final String desc, + final String signature, final Object value) { this.attrs - .add("attribute") - .set(name) - .attr("type", desc.replaceAll(";$", "")) - .attr( - "public", - (access & Opcodes.ACC_PUBLIC) == Opcodes.ACC_PUBLIC - ) - .attr( - "final", - (access & Opcodes.ACC_FINAL) == Opcodes.ACC_FINAL - ) - .attr( - "static", - (access & Opcodes.ACC_STATIC) == Opcodes.ACC_STATIC - ) - .up(); + .add("attribute") + .set(name) + .attr("type", desc.replaceAll(";$", "")) + .attr( + "public", + (access & Opcodes.ACC_PUBLIC) == Opcodes.ACC_PUBLIC + ) + .attr( + "final", + (access & Opcodes.ACC_FINAL) == Opcodes.ACC_FINAL + ) + .attr( + "static", + (access & Opcodes.ACC_STATIC) == Opcodes.ACC_STATIC + ) + .up(); return super.visitField(access, name, desc, signature, value); } @@ -140,53 +144,44 @@ public FieldVisitor visitField(final int access, // - the `visitMethodInsn` arguments. @Override @SuppressWarnings( - { - "PMD.UseVarargs", - "PMD.UseObjectForClearerAPI" - } + { + "PMD.UseVarargs", + "PMD.UseObjectForClearerAPI" + } ) public MethodVisitor visitMethod(final int access, - final String mtd, final String desc, - final String signature, final String[] exceptions) { + final String mtd, final String desc, + final String signature, final String[] exceptions) { final Directives dirs = new Directives(); if ((access & Opcodes.ACC_SYNTHETIC) != Opcodes.ACC_SYNTHETIC) { String visibility = "default"; if ((access & Opcodes.ACC_PUBLIC) == Opcodes.ACC_PUBLIC) { visibility = "public"; - } else if ( - (access & Opcodes.ACC_PROTECTED) == Opcodes.ACC_PROTECTED) { + } else if ((access & Opcodes.ACC_PROTECTED) == Opcodes.ACC_PROTECTED) { visibility = "protected"; } else if ((access & Opcodes.ACC_PRIVATE) == Opcodes.ACC_PRIVATE) { visibility = "private"; } + + // Извлечение имени переменной из описания метода + String[] parts = desc.split("\\)"); + String[] methodParts = parts[0].split("\\("); + String methodName = methodParts[0]; + String[] variables = methodParts[1].split(","); + String variableName = variables[0]; // Предполагаем, что первая переменная - это та, к которой применен вызов метода + dirs.add("method") - .attr("name", mtd) - .attr("desc", desc) - .attr( - "ctor", - "".equals(mtd) || "".equals(mtd) - ) - .attr( - "static", - (access & Opcodes.ACC_STATIC) == Opcodes.ACC_STATIC - ) - .attr( - "abstract", - (access & Opcodes.ACC_ABSTRACT) == Opcodes.ACC_ABSTRACT - ) - .attr( - "visibility", - visibility - ) - .attr( - "bridge", - (access & Opcodes.ACC_BRIDGE) == Opcodes.ACC_BRIDGE - ) - .append(new TypesOf(desc)); + .attr("name", mtd) + .attr("variableName", variableName) + .attr("desc", desc) + .attr("ctor", "".equals(mtd) || "".equals(mtd)) + .attr("static", (access & Opcodes.ACC_STATIC) == Opcodes.ACC_STATIC) + .attr("abstract", (access & Opcodes.ACC_ABSTRACT) == Opcodes.ACC_ABSTRACT) + .attr("visibility", visibility) + .attr("bridge", (access & Opcodes.ACC_BRIDGE) == Opcodes.ACC_BRIDGE) + .append(new TypesOf(desc)); this.methods.add(dirs); } - return new OpsOf( - dirs, super.visitMethod(access, mtd, desc, signature, exceptions) - ); + return new OpsOf(dirs, super.visitMethod(access, mtd, desc, signature, exceptions)); } } diff --git a/src/main/resources/org/jpeek/metrics/LCOM4.xsl b/src/main/resources/org/jpeek/metrics/LCOM4.xsl index 5e89ad62..bd696717 100644 --- a/src/main/resources/org/jpeek/metrics/LCOM4.xsl +++ b/src/main/resources/org/jpeek/metrics/LCOM4.xsl @@ -60,6 +60,18 @@ SOFTWARE. + + + + + + + + + + + + diff --git a/src/test/java/org/jpeek/XslReportTest.java b/src/test/java/org/jpeek/XslReportTest.java index c5b048f6..42db3664 100644 --- a/src/test/java/org/jpeek/XslReportTest.java +++ b/src/test/java/org/jpeek/XslReportTest.java @@ -136,6 +136,7 @@ void createsFullXmlReport(@TempDir final Path output) throws Exception { ).affirm(); } + @Test void setsCorrectSchemaLocation(@TempDir final Path output) throws Exception { new XslReport(