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 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | 83x 83x 83x 83x 83x 83x 83x 5x 47x 5x 42x 42x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 36x 5x 5x 38x 38x 27x 10x 27x 27x 27x 27x 27x 11x 1x 11x 11x 38x 38x 38x 5x 9x 1x 9x 9x 9x 19x 19x 9x 31x 31x 31x 31x 47x 31x 57x 57x 57x 33x 57x 57x 31x 5x 5x | import { CCFile, CodeMapNode, FileMeta, KeyValuePair, NodeType } from "../codeCharta.model" import { FileNameHelper } from "./fileNameHelper" import { hierarchy } from "d3-hierarchy" import packageJson from "../../../package.json" import { getParent } from "./nodePathHelper" import { fileRoot } from "../services/loadFile/fileRoot" export class DeltaGenerator { static createCodeMapFromHashMap(hashMapWithAllNodes: Map<string, CodeMapNode>) { let rootNode: CodeMapNode for (const [path, node] of hashMapWithAllNodes) { if (path === fileRoot.rootPath) { rootNode = node } else { const parentNode = getParent(hashMapWithAllNodes, path) parentNode.children.push(node) } } return rootNode } static getDeltaFile(referenceFile: CCFile, comparisonFile: CCFile) { const deltaNodesByPath = this.getDeltaNodesByPath(referenceFile.map, comparisonFile.map) const map = this.createCodeMapFromHashMap(deltaNodesByPath) const fileMeta = this.getFileMetaData(referenceFile, comparisonFile) return this.getNewCCFileWithDeltas(map, fileMeta) } private static getDeltaNodesByPath(referenceMap: CodeMapNode, comparisonMap: CodeMapNode) { const deltaNodesByPath = new Map<string, CodeMapNode>() const referenceNodesByPath = this.getReferenceNodesByPath(referenceMap) this.addExistingAndNewNodesToDeltaMap(referenceNodesByPath, comparisonMap, deltaNodesByPath) this.addDeletedNodesToDeltaMap(referenceNodesByPath, deltaNodesByPath) return deltaNodesByPath } private static getReferenceNodesByPath(referenceMap: CodeMapNode) { const referenceNodesByPath = new Map<string, CodeMapNode>() for (const { data } of hierarchy(referenceMap)) { referenceNodesByPath.set(data.path, data) } return referenceNodesByPath } private static addExistingAndNewNodesToDeltaMap( referenceNodesByPath: Map<string, CodeMapNode>, comparisonMap: CodeMapNode, deltaNodesByPath: Map<string, CodeMapNode> ) { for (const { data: comparisonNode } of hierarchy(comparisonMap)) { const referenceNode = referenceNodesByPath.get(comparisonNode.path) if (referenceNode) { if (referenceNode.children || comparisonNode.children) { referenceNode.children = [] } const { deltaList, differenceExists } = this.compareAttributeValues(referenceNode.attributes, comparisonNode.attributes) referenceNode.deltas = deltaList const changed = differenceExists ? 1 : 0 // TODO: The attributes have to be consolidated to have a single set of // attributes instead of conflicting attributes. This applies to all // attributes and is not specific about the attributes from the // reference node. referenceNode.attributes = comparisonNode.attributes referenceNode.fileCount = { added: 0, removed: 0, changed } } else { if (comparisonNode.children) { comparisonNode.children = [] } comparisonNode.deltas = { ...comparisonNode.attributes } comparisonNode.fileCount = { added: comparisonNode.type === NodeType.FILE ? 1 : 0, removed: 0, changed: 0 } } const node = referenceNode ?? comparisonNode deltaNodesByPath.set(node.path, node) referenceNodesByPath.delete(node.path) } } private static addDeletedNodesToDeltaMap(referenceNodesByPath: Map<string, CodeMapNode>, deltaNodesByPath: Map<string, CodeMapNode>) { for (const node of referenceNodesByPath.values()) { if (node.children) { node.children = [] } node.deltas = {} node.fileCount = { added: 0, removed: node.type === NodeType.FILE ? 1 : 0, changed: 0 } for (const [key, value] of Object.entries(node.attributes)) { node.deltas[key] = -value node.attributes[key] = 0 } deltaNodesByPath.set(node.path, node) } } private static compareAttributeValues(reference: KeyValuePair, comparison: KeyValuePair) { const deltaList: KeyValuePair = {} let differenceExists = false const attributeKeys = new Set(Object.keys(reference)) for (const key of Object.keys(comparison)) { attributeKeys.add(key) } for (const key of attributeKeys) { const referenceAttribute = reference[key] ?? 0 const compAttribute = comparison[key] ?? 0 if (referenceAttribute !== compAttribute) { differenceExists = true } const attributeDelta = compAttribute - referenceAttribute deltaList[key] = attributeDelta } // TODO: All entries should have the combined attributes and deltas set, // even if they do not exist on one side. Calculate these attributes up // front. This operation is otherwise costly. return { deltaList, differenceExists } } private static getFileMetaData(referenceFile: CCFile, comparisonFile: CCFile): FileMeta { return { fileName: `delta_between_${FileNameHelper.withoutCCExtension( referenceFile.fileMeta.fileName )}_and_${FileNameHelper.withoutCCExtension(comparisonFile.fileMeta.fileName)}`, fileChecksum: `${referenceFile.fileMeta.fileChecksum};${comparisonFile.fileMeta.fileChecksum}`, apiVersion: packageJson.codecharta.apiVersion, projectName: `delta_between_${referenceFile.fileMeta.projectName}_and_${comparisonFile.fileMeta.projectName}`, exportedFileSize: referenceFile.fileMeta.exportedFileSize + comparisonFile.fileMeta.exportedFileSize } } private static getNewCCFileWithDeltas(rootNode: CodeMapNode, fileMeta: FileMeta): CCFile { return { map: rootNode, fileMeta, settings: { fileSettings: { edges: [], blacklist: [], attributeTypes: { nodes: {}, edges: {} }, attributeDescriptors: {}, markedPackages: [] } } } } } |