diff --git a/src/main/java/org/jpeek/App.java b/src/main/java/org/jpeek/App.java index e356ffde..6f258741 100644 --- a/src/main/java/org/jpeek/App.java +++ b/src/main/java/org/jpeek/App.java @@ -49,6 +49,7 @@ import org.cactoos.scalar.IoChecked; import org.cactoos.scalar.LengthOf; import org.jpeek.calculus.Calculus; +import org.jpeek.calculus.java.Ccm; import org.jpeek.calculus.xsl.XslCalculus; import org.jpeek.skeleton.Skeleton; import org.xembly.Directives; @@ -250,7 +251,6 @@ private void buildReport(final Collection layers, final Collection final Base base = new DefaultBase(this.input); final XML skeleton = new Skeleton(base).xml(); final XSL chain = new XSLChain(layers); - final Calculus xsl = new XslCalculus(); this.save(skeleton.toString(), "skeleton.xml"); Arrays.stream(Metrics.values()) .filter( @@ -258,6 +258,12 @@ private void buildReport(final Collection layers, final Collection ) .forEach( metric -> { + final Calculus xsl; + if (metric == Metrics.CCM) { + xsl = new Ccm(); + } else { + xsl = new XslCalculus(); + } if (Objects.nonNull(metric.getSigma())) { reports.add( new XslReport( diff --git a/src/main/java/org/jpeek/XslReport.java b/src/main/java/org/jpeek/XslReport.java index 1418f4b8..f82311b2 100644 --- a/src/main/java/org/jpeek/XslReport.java +++ b/src/main/java/org/jpeek/XslReport.java @@ -51,7 +51,7 @@ * * @since 0.1 */ -final class XslReport implements Report { +public final class XslReport implements Report { /** * Location to the schema file. diff --git a/src/main/java/org/jpeek/calculus/java/Ccm.java b/src/main/java/org/jpeek/calculus/java/Ccm.java index a24123cc..ac32ee1f 100644 --- a/src/main/java/org/jpeek/calculus/java/Ccm.java +++ b/src/main/java/org/jpeek/calculus/java/Ccm.java @@ -24,21 +24,27 @@ package org.jpeek.calculus.java; import com.jcabi.xml.XML; +import com.jcabi.xml.XSL; +import com.jcabi.xml.XSLChain; import com.jcabi.xml.XSLDocument; +import java.util.ArrayList; import java.util.List; import java.util.Map; import org.cactoos.io.ResourceOf; import org.cactoos.io.UncheckedInput; import org.cactoos.text.FormattedText; -import org.cactoos.text.Joined; +import org.jpeek.XslReport; import org.jpeek.calculus.Calculus; +import org.jpeek.graph.Disjoint; +import org.jpeek.graph.XmlGraph; /** * CCM metric Java calculus. + * This class implements the Calculus interface to provide functionality + * for computing the CCM metric for Java code. * @since 0.30.25 */ public final class Ccm implements Calculus { - @Override public XML node( final String metric, @@ -52,57 +58,77 @@ public XML node( ).toString() ); } - return Ccm.withFixedNcc( - new XSLDocument( - new UncheckedInput( - new ResourceOf("org/jpeek/metrics/CCM.xsl") - ).stream() - ).transform(skeleton), - skeleton - ); + List layers = new ArrayList<>(0); + if (!params.containsKey("include-static-methods")) { + layers = addXslFilter(layers, "no-static-methods.xsl"); + } + if (!params.containsKey("include-ctors")) { + layers = addXslFilter(layers, "no-ctors.xsl"); + } + if (!params.containsKey("include-private-methods")) { + layers = addXslFilter(layers, "no-private-methods.xsl"); + } + final XSLChain chain = new XSLChain(layers); + final XML meta = new XSLDocument( + XslReport.class.getResourceAsStream( + "xsl/meta/meta-creater.xsl" + ) + ).transform(chain.transform(skeleton)); + final XML modified = findNcc(skeleton, meta); + return new XSLDocument( + new UncheckedInput( + new ResourceOf("org/jpeek/metrics/CCM.xsl") + ).stream() + ).transform(modified); } /** - * Updates the transformed xml with proper NCC value. - * @param transformed The transformed XML skeleton. - * @param skeleton XML Skeleton - * @return XML with fixed NCC. + * Find NCC. + * + * @param skeleton The XML skeleton to operate on + * @param meta The XML containing metadata + * @return The modified XML */ - private static XML withFixedNcc(final XML transformed, final XML skeleton) { - final List packages = transformed.nodes("//package"); - for (final XML elt : packages) { - final String pack = elt.xpath("/@id").get(0); - final List classes = elt.nodes("//class"); - for (final XML clazz : classes) { - Ccm.updateNcc(skeleton, pack, clazz); - } + private static XML findNcc(final XML skeleton, final XML meta) { + XML result = meta; + final XSL ncc = new XSLDocument( + XslReport.class.getResourceAsStream( + "xsl/meta/meta-ncc.xsl" + ) + ); + for (final XML clazz : meta.nodes("//class")) { + final XML pack = clazz.nodes("..").get(0); + final XmlGraph graph = new XmlGraph( + skeleton, + pack.xpath("@id").get(0), + clazz.xpath("@id").get(0) + ); + final String size = String.valueOf(new Disjoint(graph).value().size()); + result = ncc + .with("package", pack.xpath("@id").get(0)) + .with("class", clazz.xpath("@id").get(0)) + .with("value", size) + .transform(result); } - return transformed; + return new XSLDocument( + XslReport.class.getResourceAsStream( + "xsl/meta/skeleton-appender.xsl" + ) + ).with("meta", result.node()).transform(skeleton); } /** - * Updates the xml node of the class with proper NCC value. - * @param skeleton XML Skeleton - * @param pack Package name - * @param clazz Class node in the resulting xml - * @todo #449:30min Implement NCC calculation with `XmlGraph` and use this - * class to fix CCM metric (see issue #449). To do this, this class, once - * it works correctly, should be integrated with XSL based calculuses in - * `XslReport` (see `todo #449` in Calculus). Write a test to make sure - * the metric is calculated correctly. Also, decide whether the - * whole CCM metric should be implemented in Java, or only the NCC part. - * Update this `todo` accordingly. + * Adds an XSL filter to the list of layers. + * + * @param layers The list of XSL filters + * @param name The name of the XSL file to append + * @return The updated list of XSL filters */ - private static void updateNcc( - final XML skeleton, final String pack, final XML clazz - ) { - throw new UnsupportedOperationException( - new Joined( - "", - skeleton.toString(), - pack, - clazz.toString() - ).toString() + private static List addXslFilter(final List layers, final String name) { + final XSLDocument doc = new XSLDocument( + XslReport.class.getResourceAsStream(String.format("xsl/layers/%s", name)) ); + layers.add(doc); + return layers; } } diff --git a/src/main/java/org/jpeek/graph/Disjoint.java b/src/main/java/org/jpeek/graph/Disjoint.java index 5879eda6..9709aeed 100644 --- a/src/main/java/org/jpeek/graph/Disjoint.java +++ b/src/main/java/org/jpeek/graph/Disjoint.java @@ -49,7 +49,7 @@ public Disjoint(final Graph graph) { } @Override - public List> value() throws Exception { + public List> value() { final Set unvisited = new HashSet<>(this.graph.nodes()); final List> result = new ArrayList<>(unvisited.size()); while (!unvisited.isEmpty()) { diff --git a/src/main/java/org/jpeek/graph/XmlGraph.java b/src/main/java/org/jpeek/graph/XmlGraph.java index c912e811..937ef10c 100644 --- a/src/main/java/org/jpeek/graph/XmlGraph.java +++ b/src/main/java/org/jpeek/graph/XmlGraph.java @@ -31,7 +31,6 @@ import org.cactoos.scalar.Sticky; import org.cactoos.scalar.Unchecked; import org.cactoos.text.FormattedText; -import org.jpeek.skeleton.Skeleton; /** * Graph implementation built on skeleton. @@ -54,7 +53,7 @@ public final class XmlGraph implements Graph { * @param pname Package of the class this graph is for * @param cname Class in the skeleton this graph is for */ - public XmlGraph(final Skeleton skeleton, final String pname, final String cname) { + public XmlGraph(final XML skeleton, final String pname, final String cname) { this.nds = new Unchecked<>( new Sticky<>( () -> XmlGraph.build(skeleton, pname, cname) @@ -74,13 +73,13 @@ 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, + private static List build(final XML skeleton, final String pname, final String cname) { final Map byxml = new MapOf<>( method -> method, method -> new Node.Simple( new XmlMethodSignature( - skeleton.xml() + skeleton .nodes( new FormattedText( "//package[@id='%s']", pname @@ -94,7 +93,7 @@ private static List build(final Skeleton skeleton, final String pname, method ).asString() ), - skeleton.xml().nodes( + skeleton.nodes( "//methods/method[@ctor='false' and @abstract='false']" ) ); diff --git a/src/main/resources/org/jpeek/metrics/CCM.xsl b/src/main/resources/org/jpeek/metrics/CCM.xsl index 2e17932e..4c9903ee 100644 --- a/src/main/resources/org/jpeek/metrics/CCM.xsl +++ b/src/main/resources/org/jpeek/metrics/CCM.xsl @@ -23,6 +23,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --> + @@ -40,7 +41,11 @@ SOFTWARE. + + + + @@ -62,7 +67,7 @@ SOFTWARE. - + diff --git a/src/main/resources/org/jpeek/xsl/meta/meta-creater.xsl b/src/main/resources/org/jpeek/xsl/meta/meta-creater.xsl new file mode 100644 index 00000000..52bfcc78 --- /dev/null +++ b/src/main/resources/org/jpeek/xsl/meta/meta-creater.xsl @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/org/jpeek/xsl/meta/meta-ncc.xsl b/src/main/resources/org/jpeek/xsl/meta/meta-ncc.xsl new file mode 100644 index 00000000..acc5aeef --- /dev/null +++ b/src/main/resources/org/jpeek/xsl/meta/meta-ncc.xsl @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/org/jpeek/xsl/meta/skeleton-appender.xsl b/src/main/resources/org/jpeek/xsl/meta/skeleton-appender.xsl new file mode 100644 index 00000000..c69d5c3c --- /dev/null +++ b/src/main/resources/org/jpeek/xsl/meta/skeleton-appender.xsl @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/org/jpeek/MetricsTest.java b/src/test/java/org/jpeek/MetricsTest.java index 17b52fd0..8548b4e1 100644 --- a/src/test/java/org/jpeek/MetricsTest.java +++ b/src/test/java/org/jpeek/MetricsTest.java @@ -27,6 +27,8 @@ import java.nio.file.Path; import org.cactoos.text.FormattedText; import org.cactoos.text.TextOf; +import org.jpeek.calculus.Calculus; +import org.jpeek.calculus.java.Ccm; import org.jpeek.calculus.xsl.XslCalculus; import org.jpeek.skeleton.Skeleton; import org.junit.jupiter.api.io.TempDir; @@ -70,8 +72,14 @@ final class MetricsTest { void testsTarget(final String target, final String metric, final double value, @TempDir final Path output) throws Exception { + final Calculus calculus; + if (metric.equals("CCM")) { + calculus = new Ccm(); + } else { + calculus = new XslCalculus(); + } new XslReport( - new Skeleton(new FakeBase(target)).xml(), new XslCalculus(), + new Skeleton(new FakeBase(target)).xml(), calculus, new ReportData(metric) ).save(output); final String xpath; diff --git a/src/test/java/org/jpeek/graph/XmlGraphTest.java b/src/test/java/org/jpeek/graph/XmlGraphTest.java index 87f4dfcf..087d35ca 100644 --- a/src/test/java/org/jpeek/graph/XmlGraphTest.java +++ b/src/test/java/org/jpeek/graph/XmlGraphTest.java @@ -76,7 +76,7 @@ final class XmlGraphTest { @Test void buildsMethodsAsNodes() { final List nodes = new XmlGraph( - new Skeleton(new FakeBase(XmlGraphTest.CLASS_NAME)), + new Skeleton(new FakeBase(XmlGraphTest.CLASS_NAME)).xml(), "", XmlGraphTest.CLASS_NAME ).nodes(); new Assertion<>( @@ -110,7 +110,7 @@ void buildsConnections() { Node::name, node -> node, new XmlGraph( - new Skeleton(new FakeBase(XmlGraphTest.CLASS_NAME)), + new Skeleton(new FakeBase(XmlGraphTest.CLASS_NAME)).xml(), "", XmlGraphTest.CLASS_NAME ).nodes() ); diff --git a/src/test/java/org/jpeek/metrics/CcmTest.java b/src/test/java/org/jpeek/metrics/CcmTest.java index 2d68f9bb..7fc3af30 100644 --- a/src/test/java/org/jpeek/metrics/CcmTest.java +++ b/src/test/java/org/jpeek/metrics/CcmTest.java @@ -23,28 +23,37 @@ */ package org.jpeek.metrics; +import com.jcabi.xml.XML; +import java.util.HashMap; +import org.jpeek.FakeBase; +import org.jpeek.calculus.java.Ccm; +import org.jpeek.skeleton.Skeleton; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; /** * Tests to check CCM metric in different links between attributes and methods. + * * @since 0.29 */ -@Disabled final class CcmTest { /** * Class with one method access one attribute have * ncc metric = methods count. + * * @throws Exception */ @Test + @Disabled void manyComponentInClassTest() throws Exception { - final MetricBase.Report report = new MetricBase( - "org/jpeek/metrics/CCM.xsl" - ).transform( - "CcmManyComp" + final XML result = new Ccm().node( + "ccm", new HashMap<>(0), + new Skeleton( + new FakeBase("CcmManyComp") + ).xml() ); + final MetricBase.Report report = new MetricBase.Report("CcmManyComp", result); report.assertVariable("methods", 5); report.assertVariable("nc", 0); report.assertVariable("nmp", 10); @@ -56,15 +65,19 @@ void manyComponentInClassTest() throws Exception { * Class with one method access one attribute and * Ctor with all attributes initialization have the same * metric as without Ctor. + * * @throws Exception */ @Test + @Disabled void manyComponentWithCtorInClassTest() throws Exception { - final MetricBase.Report report = new MetricBase( - "org/jpeek/metrics/CCM.xsl" - ).transform( - "CcmManyCompWithCtor" + final XML result = new Ccm().node( + "ccm", new HashMap<>(0), + new Skeleton( + new FakeBase("CcmManyCompWithCtor") + ).xml() ); + final MetricBase.Report report = new MetricBase.Report("CcmManyCompWithCtor", result); report.assertVariable("methods", 5); report.assertVariable("nc", 0); report.assertVariable("nmp", 10); @@ -73,12 +86,15 @@ void manyComponentWithCtorInClassTest() throws Exception { } @Test + @Disabled void oneComponentInClassTest() throws Exception { - final MetricBase.Report report = new MetricBase( - "org/jpeek/metrics/CCM.xsl" - ).transform( - "CcmOneComp" + final XML result = new Ccm().node( + "ccm", new HashMap<>(0), + new Skeleton( + new FakeBase("CcmOneComp") + ).xml() ); + final MetricBase.Report report = new MetricBase.Report("CcmOneComp", result); report.assertVariable("methods", 5); report.assertVariable("nc", 10); report.assertVariable("nmp", 10); @@ -88,19 +104,24 @@ void oneComponentInClassTest() throws Exception { /** * Check ccm metric for mixed usage: attribute usage, methods calls. + * + * @throws Exception * @todo #522:30min there is a 4th step for incorrect calculation: nc * in case of calling one method from another because of * `xsl:if test="$method/ops/op/text()[. = $other/ops/op/text()]"` * method name is not used for creating edge. - * @throws Exception */ @Test + @Disabled void mixedCallsInClassTest() throws Exception { - final MetricBase.Report report = new MetricBase( - "org/jpeek/metrics/CCM.xsl" - ).transform( - "CcmMixCallManyComp" + final XML result = new Ccm().node( + "ccm", + new HashMap<>(0), + new Skeleton( + new FakeBase("CcmMixCallManyComp") + ).xml() ); + final MetricBase.Report report = new MetricBase.Report("CcmMixCallManyComp", result); report.assertVariable("methods", 5); report.assertVariable("nc", 2); report.assertVariable("nmp", 10); diff --git a/src/test/resources/org/jpeek/metricstest-params.csv b/src/test/resources/org/jpeek/metricstest-params.csv index a8684aa7..389b590a 100644 --- a/src/test/resources/org/jpeek/metricstest-params.csv +++ b/src/test/resources/org/jpeek/metricstest-params.csv @@ -114,9 +114,9 @@ WithoutAttributes,CCM,NaN OneMethodCreatesLambda,CCM,NaN OneVoidMethodWithoutParams,CCM,NaN #Bar,CCM,0.2222d -Foo,CCM,0.5d -OverloadMethods,CCM,1.0d -TwoCommonAttributes,CCM,NaN +Foo,CCM,1.0d +OverloadMethods,CCM,0.5d +TwoCommonAttributes,CCM,0.0d #TwoCommonMethods,CCM,0.0333d Bar,MWE,1.0d Foo,MWE,1.0d