All files / app/codeCharta/ui/fileExtensionBar/selectors fileExtensionCalculator.ts

98.21% Statements 55/56
89.47% Branches 17/19
100% Functions 8/8
98.14% Lines 53/54

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124  3x 3x 3x                 3x 3x 3x 3x     7x       7x 7x   7x 55x 39x 39x 39x 39x   39x 10x   29x       7x 1x     6x 6x 27x 27x 27x     38x 6x 6x       6x 6x   6x 27x 24x   3x 3x       6x 1x     6x       6x                 29x                 44x 44x 37x 37x   7x       1x                 36x 13x     23x 23x 79x   23x      
import { CodeMapNode } from "../../../codeCharta.model"
import { hierarchy } from "d3-hierarchy"
import { HSL } from "../../../util/color/hsl"
import { isLeaf } from "../../../util/codeMapHelper"
 
export interface MetricDistribution {
    fileExtension: string
    absoluteMetricValue: number
    relativeMetricValue: number
    color: string
}
 
export class FileExtensionCalculator {
    private static readonly NO_EXTENSION = "None"
    private static readonly OTHER_EXTENSION = "other"
    private static OTHER_GROUP_THRESHOLD_VALUE = 3
 
    static getMetricDistribution(map: CodeMapNode, metric: string): MetricDistribution[] {
        Iif (!map) {
            return []
        }
 
        const distributions: Map<string, MetricDistribution> = new Map()
        let sumOfAllMetricValues = 0
 
        for (const node of hierarchy(map)) {
            if (isLeaf(node) && !node.data.isExcluded) {
                const metricValue = node.data.attributes[metric]
                const fileExtension = FileExtensionCalculator.estimateFileExtension(node.data.name)
                const matchingFileExtensionObject = distributions.get(fileExtension)
                sumOfAllMetricValues += metricValue
 
                if (matchingFileExtensionObject) {
                    matchingFileExtensionObject.absoluteMetricValue += metricValue
                } else {
                    distributions.set(fileExtension, FileExtensionCalculator.getDistributionObject(fileExtension, metricValue))
                }
            }
        }
        if (sumOfAllMetricValues === 0) {
            return [FileExtensionCalculator.getNoneExtension()]
        }
 
        let metrics = []
        for (const distribution of distributions.values()) {
            if (distribution.absoluteMetricValue !== 0) {
                distribution.relativeMetricValue = (distribution.absoluteMetricValue * 100) / sumOfAllMetricValues
                metrics.push(distribution)
            }
        }
        metrics.sort((a, b) => b.absoluteMetricValue - a.absoluteMetricValue)
        metrics = FileExtensionCalculator.getMetricDistributionWithOthers(metrics)
        return metrics.length > 0 ? metrics : [FileExtensionCalculator.getNoneExtension()]
    }
 
    private static getMetricDistributionWithOthers(distribution: MetricDistribution[]) {
        const otherExtension = FileExtensionCalculator.getOtherExtension()
        const visibleDistributions: MetricDistribution[] = []
 
        for (const metric of distribution) {
            if (metric.relativeMetricValue > FileExtensionCalculator.OTHER_GROUP_THRESHOLD_VALUE) {
                visibleDistributions.push(metric)
            } else {
                otherExtension.absoluteMetricValue += metric.absoluteMetricValue
                otherExtension.relativeMetricValue += metric.relativeMetricValue
            }
        }
 
        if (otherExtension.relativeMetricValue > 0) {
            visibleDistributions.push(otherExtension)
        }
 
        return visibleDistributions
    }
 
    private static getOtherExtension(): MetricDistribution {
        return {
            fileExtension: FileExtensionCalculator.OTHER_EXTENSION,
            absoluteMetricValue: 0,
            relativeMetricValue: 0,
            color: FileExtensionCalculator.getColor(FileExtensionCalculator.OTHER_EXTENSION)
        }
    }
 
    private static getDistributionObject(fileExtension: string, metricValue: number): MetricDistribution {
        return {
            fileExtension,
            absoluteMetricValue: metricValue,
            relativeMetricValue: 0,
            color: FileExtensionCalculator.getColor(fileExtension)
        }
    }
 
    static estimateFileExtension(fileName: string) {
        const lastDotPosition = fileName.lastIndexOf(".")
        if (lastDotPosition > 0 && lastDotPosition !== fileName.length) {
            const extension = fileName.slice(lastDotPosition + 1)
            return extension.toLowerCase()
        }
        return FileExtensionCalculator.NO_EXTENSION
    }
 
    private static getNoneExtension(): MetricDistribution {
        return {
            fileExtension: FileExtensionCalculator.NO_EXTENSION,
            absoluteMetricValue: null,
            relativeMetricValue: 100,
            color: FileExtensionCalculator.getColor(FileExtensionCalculator.NO_EXTENSION)
        }
    }
 
    private static getColor(fileExtension: string): string {
        if (fileExtension === FileExtensionCalculator.NO_EXTENSION || fileExtension === FileExtensionCalculator.OTHER_EXTENSION) {
            return "#676867"
        }
 
        let hash = 0
        for (let index = 0; index < fileExtension.length; index++) {
            hash = fileExtension.codePointAt(index) + ((hash << 5) - hash)
        }
        return new HSL(hash % 360, 60, 50).toString()
    }
}