JSONMetricsReport.java

  1. /*
  2.  *  Copyright (c) 2001-2024, Jean Tessier
  3.  *  All rights reserved.
  4.  *  
  5.  *  Redistribution and use in source and binary forms, with or without
  6.  *  modification, are permitted provided that the following conditions
  7.  *  are met:
  8.  *  
  9.  *      * Redistributions of source code must retain the above copyright
  10.  *        notice, this list of conditions and the following disclaimer.
  11.  *  
  12.  *      * Redistributions in binary form must reproduce the above copyright
  13.  *        notice, this list of conditions and the following disclaimer in the
  14.  *        documentation and/or other materials provided with the distribution.
  15.  *  
  16.  *      * Neither the name of Jean Tessier nor the names of his contributors
  17.  *        may be used to endorse or promote products derived from this software
  18.  *        without specific prior written permission.
  19.  *  
  20.  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21.  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22.  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23.  *  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
  24.  *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  25.  *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  26.  *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  27.  *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  28.  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  29.  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  30.  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31.  */

  32. package com.jeantessier.dependency;

  33. import java.io.*;
  34. import java.util.*;

  35. import static java.util.stream.Collectors.*;

  36. public class JSONMetricsReport extends MetricsReport {
  37.     public JSONMetricsReport(PrintWriter out) {
  38.         super(out);
  39.     }

  40.     public void process(MetricsGatherer metrics) {
  41.         print("{");

  42.         printProgrammingElementStats(metrics);
  43.         print(",");
  44.         printDependencyStats(metrics);

  45.         if (isShowingClassesPerPackageHistogram() ||
  46.                 isShowingFeaturesPerClassHistogram() ||
  47.                 isShowingInboundsPerPackageHistogram() ||
  48.                 isShowingOutboundsPerPackageHistogram() ||
  49.                 isShowingInboundsPerClassHistogram() ||
  50.                 isShowingOutboundsPerClassHistogram() ||
  51.                 isShowingInboundsPerFeatureHistogram() ||
  52.                 isShowingOutboundsPerFeatureHistogram()) {
  53.             print(",");
  54.             printHistograms(metrics);
  55.         }

  56.         if (isShowingClassesPerPackageChart() ||
  57.                 isShowingFeaturesPerClassChart() ||
  58.                 isShowingInboundsPerPackageChart() ||
  59.                 isShowingOutboundsPerPackageChart() ||
  60.                 isShowingInboundsPerClassChart() ||
  61.                 isShowingOutboundsPerClassChart() ||
  62.                 isShowingInboundsPerFeatureChart() ||
  63.                 isShowingOutboundsPerFeatureChart()) {
  64.             print(",");
  65.             printChart(metrics);
  66.         }

  67.         print("}");
  68.         println();
  69.     }

  70.     private void printProgrammingElementStats(MetricsGatherer metrics) {
  71.         printProgrammingElementStats("packages", metrics.getPackages());

  72.         print(",");

  73.         printProgrammingElementStats("classes", metrics.getClasses());

  74.         print(",");

  75.         printProgrammingElementStats("features", metrics.getFeatures());
  76.     }

  77.     private void printProgrammingElementStats(String label, Collection<? extends Node> nodes) {
  78.         var nbElements = nodes.size();
  79.         var nbConfirmedElements = countConfirmedNodes(nodes);
  80.         var ratio = nbConfirmedElements / (double) nbElements;

  81.         print("\"" + label + "\":{");
  82.         print("\"count\":" + nbElements);
  83.         print(",");
  84.         print("\"confirmed\":" + nbConfirmedElements);
  85.         print(",");
  86.         print("\"ratio\":" + formatValue(ratio));
  87.         if (isListingElements()) {
  88.             print(",");
  89.             print("\"elements\": [");
  90.             print(renderNodes(nodes));
  91.             print("]");
  92.         }
  93.         print("}");
  94.     }

  95.     private String renderNodes(Collection<? extends Node> nodes) {
  96.         return nodes.stream()
  97.                 .sorted()
  98.                 .map(node -> "{\"name\":\"" + node.getName() + "\",\"simpleName\":\"" + node.getSimpleName() + "\",\"confirmed\":" + node.isConfirmed() + "}")
  99.                 .collect(joining(","));
  100.     }

  101.     private void printDependencyStats(MetricsGatherer metrics) {
  102.         print("\"outbounds\": {");
  103.         print("\"packages\":" + metrics.getNbOutboundPackages() + ",");
  104.         print("\"packageRatio\":" + formatValue(metrics.getNbOutboundPackages() / (double) metrics.getPackages().size()) + ",");
  105.         print("\"classes\":" + metrics.getNbOutboundClasses() + ",");
  106.         print("\"classRatio\":" + formatValue(metrics.getNbOutboundClasses() / (double) metrics.getClasses().size()) + ",");
  107.         print("\"features\":" + metrics.getNbOutboundFeatures() + ",");
  108.         print("\"featureRatio\":" + formatValue(metrics.getNbOutboundFeatures() / (double) metrics.getFeatures().size()) + ",");
  109.         print("\"total\":" + metrics.getNbOutbound());
  110.         print("}");

  111.         print(",");

  112.         print("\"inbounds\": {");
  113.         print("\"packages\":" + metrics.getNbInboundPackages() + ",");
  114.         print("\"packageRatio\":" + formatValue(metrics.getNbInboundPackages() / (double) metrics.getPackages().size()) + ",");
  115.         print("\"classes\":" + metrics.getNbInboundClasses() + ",");
  116.         print("\"classRatio\":" + formatValue(metrics.getNbInboundClasses() / (double) metrics.getClasses().size()) + ",");
  117.         print("\"features\":" + metrics.getNbInboundFeatures() + ",");
  118.         print("\"featureRatio\":" + formatValue(metrics.getNbInboundFeatures() / (double) metrics.getFeatures().size()) + ",");
  119.         print("\"total\":" + metrics.getNbInbound());
  120.         print("}");
  121.     }

  122.     private void printHistograms(MetricsGatherer metrics) {
  123.         var histograms = new HashMap<String, Map<Long, Long>>();

  124.         if (isShowingClassesPerPackageHistogram()) {
  125.             histograms.put("classesPerPackage", metrics.getHistogram(MetricsGatherer.CLASSES_PER_PACKAGE));
  126.         }

  127.         if (isShowingFeaturesPerClassHistogram()) {
  128.             histograms.put("featuresPerClass", metrics.getHistogram(MetricsGatherer.FEATURES_PER_CLASS));
  129.         }

  130.         if (isShowingInboundsPerPackageHistogram()) {
  131.             histograms.put("inboundsPerPackage", metrics.getHistogram(MetricsGatherer.INBOUNDS_PER_PACKAGE));
  132.         }

  133.         if (isShowingOutboundsPerPackageHistogram()) {
  134.             histograms.put("outboundsPerPackage", metrics.getHistogram(MetricsGatherer.OUTBOUNDS_PER_PACKAGE));
  135.         }

  136.         if (isShowingInboundsPerClassHistogram()) {
  137.             histograms.put("inboundsPerClass", metrics.getHistogram(MetricsGatherer.OUTBOUNDS_PER_CLASS));
  138.         }

  139.         if (isShowingOutboundsPerClassHistogram()) {
  140.             histograms.put("outboundsPerClass", metrics.getHistogram(MetricsGatherer.OUTBOUNDS_PER_CLASS));
  141.         }

  142.         if (isShowingInboundsPerFeatureHistogram()) {
  143.             histograms.put("inboundsPerFeature", metrics.getHistogram(MetricsGatherer.OUTBOUNDS_PER_FEATURE));
  144.         }

  145.         if (isShowingOutboundsPerFeatureHistogram()) {
  146.             histograms.put("outboundsPerFeature", metrics.getHistogram(MetricsGatherer.OUTBOUNDS_PER_FEATURE));
  147.         }

  148.         print("\"histograms\":{");
  149.         print(
  150.                 histograms.entrySet().stream()
  151.                         .map(entry ->
  152.                                 "\"" + entry.getKey() + "\":[" + new TreeMap<>(entry.getValue()).entrySet().stream().map(pair -> "[" + pair.getKey() + "," + pair.getValue() + "]").collect(joining(",")) + "]")
  153.                         .collect(joining(","))
  154.         );
  155.         print("}");
  156.     }

  157.     private void printChart(MetricsGatherer metrics) {
  158.         print("\"chart\":[");

  159.         // Headings
  160.         print("[");
  161.         print("\"n\"");
  162.         if (isShowingClassesPerPackageChart()) {
  163.             print(",\"classes per package\"");
  164.         }
  165.         if (isShowingFeaturesPerClassChart()) {
  166.             print(",\"features per class\"");
  167.         }
  168.         if (isShowingInboundsPerPackageChart()) {
  169.             print(",\"inbounds per package\"");
  170.         }
  171.         if (isShowingOutboundsPerPackageChart()) {
  172.             print(",\"outbounds per package\"");
  173.         }
  174.         if (isShowingInboundsPerClassChart()) {
  175.             print(",\"inbounds per class\"");
  176.         }
  177.         if (isShowingOutboundsPerClassChart()) {
  178.             print(",\"outbounds per class\"");
  179.         }
  180.         if (isShowingInboundsPerFeatureChart()) {
  181.             print(",\"inbounds per feature\"");
  182.         }
  183.         if (isShowingOutboundsPerFeatureChart()) {
  184.             print(",\"outbounds per feature\"");
  185.         }
  186.         print("]");

  187.         // Data
  188.         for (int k=0; k<=metrics.getChartMaximum(); k++) {
  189.             long[] dataPoint = metrics.getChartData(k);

  190.             print(",[");
  191.             print(k);
  192.             if (isShowingClassesPerPackageChart()) {
  193.                 print("," + dataPoint[MetricsGatherer.CLASSES_PER_PACKAGE]);
  194.             }
  195.             if (isShowingFeaturesPerClassChart()) {
  196.                 print("," + dataPoint[MetricsGatherer.FEATURES_PER_CLASS]);
  197.             }
  198.             if (isShowingInboundsPerPackageChart()) {
  199.                 print("," + dataPoint[MetricsGatherer.INBOUNDS_PER_PACKAGE]);
  200.             }
  201.             if (isShowingOutboundsPerPackageChart()) {
  202.                 print("," + dataPoint[MetricsGatherer.OUTBOUNDS_PER_PACKAGE]);
  203.             }
  204.             if (isShowingInboundsPerClassChart()) {
  205.                 print("," + dataPoint[MetricsGatherer.INBOUNDS_PER_CLASS]);
  206.             }
  207.             if (isShowingOutboundsPerClassChart()) {
  208.                 print("," + dataPoint[MetricsGatherer.OUTBOUNDS_PER_CLASS]);
  209.             }
  210.             if (isShowingInboundsPerFeatureChart()) {
  211.                 print("," + dataPoint[MetricsGatherer.INBOUNDS_PER_FEATURE]);
  212.             }
  213.             if (isShowingOutboundsPerFeatureChart()) {
  214.                 print("," + dataPoint[MetricsGatherer.OUTBOUNDS_PER_FEATURE]);
  215.             }
  216.             print("]");
  217.         }

  218.         print("]");
  219.     }

  220.     private String formatValue(double value) {
  221.         if (Double.isNaN(value) || Double.isInfinite(value)) {
  222.             return "null";
  223.         }

  224.         return String.valueOf(value);
  225.     }
  226. }