org.codeqinvest.web.project.InvestmentOpportunitiesJsonGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.codeqinvest.web.project.InvestmentOpportunitiesJsonGenerator.java

Source

/*
 * Copyright 2013 - 2014 Felix Mller
 *
 * This file is part of CodeQ Invest.
 *
 * CodeQ Invest is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * CodeQ Invest is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with CodeQ Invest.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.codeqinvest.web.project;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Splitter;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import org.codeqinvest.investment.profit.WeightedProfitCalculator;
import org.codeqinvest.quality.Artefact;
import org.codeqinvest.quality.QualityViolation;
import org.codeqinvest.quality.analysis.QualityAnalysis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

/**
 * The generator produces the necessary json tree for the
 * investment opportunities that are displayed on the project template
 * in a tree map.
 *
 * @author fmueller
 */
@Component
public class InvestmentOpportunitiesJsonGenerator {

    private static final ObjectMapper MAPPER = new ObjectMapper();
    private static final Splitter PACKAGE_SPLITTER = Splitter.on('.');

    private final WeightedProfitCalculator weightedProfitCalculator;

    @Autowired
    InvestmentOpportunitiesJsonGenerator(WeightedProfitCalculator weightedProfitCalculator) {
        this.weightedProfitCalculator = weightedProfitCalculator;
    }

    public String generate(QualityAnalysis analysis) throws JsonProcessingException {
        Set<String> alreadyAddedArtefacts = Sets.newHashSet();
        Map<String, PackageNode> nodeLookupTable = Maps.newHashMap();
        RootNode rootNode = new RootNode(analysis.getProject().getName());

        for (QualityViolation violation : analysis.getViolations()) {
            addArtefact(violation, rootNode, alreadyAddedArtefacts, nodeLookupTable);
        }

        rootNode.filterProfitableChildren();
        rootNode.updateChangeProbabilityOfProfitableChildren();
        rootNode.updateAutomaticChangeProbabilityAndEstimateOfAllChildren();
        return MAPPER.writeValueAsString(rootNode);
    }

    private void addArtefact(QualityViolation violation, RootNode root, Set<String> alreadyAddedArtefacts,
            Map<String, PackageNode> nodeLookupTable) {
        Artefact artefact = violation.getArtefact();
        if (!alreadyAddedArtefacts.contains(artefact.getName())) {

            alreadyAddedArtefacts.add(artefact.getName());
            double weightedProfit = weightedProfitCalculator.calculateWeightedProfit(violation);
            ArtefactNode artefactNode = new ArtefactNode(artefact, weightedProfit);

            PackageNode currentPackageNode = null;
            for (String packageName : getAllPackageNamesReversed(artefact)) {
                if (nodeLookupTable.containsKey(packageName)) {
                    PackageNode node = nodeLookupTable.get(packageName);
                    if (currentPackageNode != null) {
                        node.addChildren(currentPackageNode);
                    } else {
                        node.addChildren(artefactNode);
                    }
                    return;
                } else {
                    PackageNode packageNode = new PackageNode(getLastPackageName(packageName), packageName);
                    nodeLookupTable.put(packageName, packageNode);
                    if (currentPackageNode != null) {
                        packageNode.addChildren(currentPackageNode);
                    } else {
                        packageNode.addChildren(artefactNode);
                    }
                    currentPackageNode = packageNode;
                }
            }

            if (currentPackageNode != null) {
                root.addChildren(currentPackageNode);
            } else {
                root.addChildren(artefactNode);
            }
        }
    }

    private String getLastPackageName(String packageName) {
        String last = null;
        for (String name : PACKAGE_SPLITTER.split(packageName)) {
            last = name;
        }
        return last;
    }

    private List<String> getAllPackageNamesReversed(Artefact artefact) {
        List<String> packages = new ArrayList<String>();
        StringBuilder packagePath = new StringBuilder();
        for (String packageName : PACKAGE_SPLITTER.split(artefact.getName())) {
            if (packageName.equals(artefact.getShortClassName())) {
                break;
            }
            if (packagePath.length() > 0) {
                packagePath.append('.');
            }
            packagePath.append(packageName);
            packages.add(packagePath.toString());
        }
        Collections.reverse(packages);
        return packages;
    }

    @Getter
    @EqualsAndHashCode
    @JsonPropertyOrder({ "name", "longName", "changeProbability", "automaticChangeProbability", "manualEstimate",
            "allChildren", "children" })
    private static class PackageNode {

        private final String name;
        private final String longName;

        private final Set<PackageNode> allChildren = new TreeSet<PackageNode>(new ByNameComparator());
        private final Set<PackageNode> children = new TreeSet<PackageNode>(new ByNameComparator());

        @Setter
        private int changeProbability;

        @Setter
        private int automaticChangeProbability;

        @Setter
        private Integer manualEstimate;

        PackageNode(String name, String longName) {
            this.name = name;
            this.longName = longName;
        }

        void addChildren(PackageNode node) {
            allChildren.add(node);
        }

        void filterProfitableChildren() {
            for (PackageNode child : getAllChildren()) {
                child.filterProfitableChildren();
                if (child.aggregateProfit() > 0.0) {
                    children.add(child);
                }
            }
        }

        double aggregateProfit() {
            double profit = 0.0;
            for (PackageNode child : getAllChildren()) {
                double packageProfit = child.aggregateProfit();
                if (packageProfit > 0.0) {
                    profit += packageProfit;
                }
            }
            return profit;
        }

        void updateChangeProbabilityOfProfitableChildren() {
            float sumOfChangeProbabilities = 0.0f;
            for (PackageNode node : getChildren()) {
                node.updateChangeProbabilityOfProfitableChildren();
                sumOfChangeProbabilities += node.getChangeProbability();
            }
            changeProbability = Math.round(sumOfChangeProbabilities / (float) getChildren().size());
        }

        void updateAutomaticChangeProbabilityAndEstimateOfAllChildren() {
            float sumOfAutomaticChangeProbabilities = 0.0f;

            Integer manualEstimateOfOneChildren = null;
            boolean isFirstEstimate = true;
            boolean hasEachChildrenSameManualEstimate = true;

            for (PackageNode node : getAllChildren()) {
                node.updateAutomaticChangeProbabilityAndEstimateOfAllChildren();

                if (manualEstimateOfOneChildren == null) {
                    if (isFirstEstimate) {
                        manualEstimateOfOneChildren = node.getManualEstimate();
                    } else if (node.getManualEstimate() != null) {
                        hasEachChildrenSameManualEstimate = false;
                    }
                } else if (!manualEstimateOfOneChildren.equals(node.getManualEstimate())
                        || node.getManualEstimate() == null) {
                    hasEachChildrenSameManualEstimate = false;
                }

                isFirstEstimate = false;
                sumOfAutomaticChangeProbabilities += node.getAutomaticChangeProbability();
            }
            automaticChangeProbability = Math
                    .round(sumOfAutomaticChangeProbabilities / (float) getAllChildren().size());

            if (hasEachChildrenSameManualEstimate && manualEstimateOfOneChildren != null) {
                manualEstimate = manualEstimateOfOneChildren;
            }
        }
    }

    @EqualsAndHashCode(callSuper = true)
    @JsonPropertyOrder({ "name", "allChildren", "children" })
    @JsonIgnoreProperties({ "longName", "value", "changeProbability", "automaticChangeProbability",
            "manualEstimate" })
    private static class RootNode extends PackageNode {

        RootNode(String name) {
            super(name, "");
        }
    }

    @Getter
    @EqualsAndHashCode(callSuper = true)
    @JsonPropertyOrder({ "name", "longName", "value", "changeProbability", "automaticChangeProbability",
            "manualEstimate" })
    @JsonIgnoreProperties({ "allChildren", "children" })
    private static class ArtefactNode extends PackageNode {

        private final double value;

        ArtefactNode(Artefact artefact, double value) {
            super(artefact.getShortClassName(), artefact.getName());
            this.value = value;

            final int automaticChangeProbability = (int) Math.round(artefact.getChangeProbability() * 100);
            setManualEstimate(artefact.getManualEstimate());
            setAutomaticChangeProbability(automaticChangeProbability);

            if (artefact.hasManualEstimate()) {
                setChangeProbability(artefact.getManualEstimate());
            } else {
                setChangeProbability(automaticChangeProbability);
            }
        }

        @Override
        double aggregateProfit() {
            return value;
        }

        @Override
        void addChildren(PackageNode node) {
            throw new UnsupportedOperationException();
        }

        @Override
        void updateChangeProbabilityOfProfitableChildren() {
            // do nothing
        }

        @Override
        void updateAutomaticChangeProbabilityAndEstimateOfAllChildren() {
            // do nothing
        }
    }

    private static final class ByNameComparator implements Comparator<PackageNode> {

        @Override
        public int compare(PackageNode node, PackageNode otherNode) {
            return node.getName().compareToIgnoreCase(otherNode.getName());
        }
    }
}