All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.com.saveourtool.save.demo.cpg.CpgGraph.kt Maven / Gradle / Ivy

The newest version!
/**
 * File that contains CpgGraph definition
 */

package com.saveourtool.save.demo.cpg

import kotlin.random.Random
import kotlinx.serialization.EncodeDefault
import kotlinx.serialization.EncodeDefault.Mode.ALWAYS
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable

/**
 * @property nodes list of [CpgNode]s
 * @property edges list of [CpgEdge]s
 * @property attributes graph attributes
 * @property options
 */
@Serializable
@ExperimentalSerializationApi
data class CpgGraph(
    val nodes: List = emptyList(),
    val edges: List = emptyList(),
    @EncodeDefault(ALWAYS) val attributes: CpgGraphAttributes = CpgGraphAttributes(),
    @EncodeDefault(ALWAYS) val options: CpgGraphOptions = CpgGraphOptions(),
) {
    /**
     * Returns graph with parallel edges combined into one (fixes labels issue)
     *
     * @return Cpg graph with removed parallel edges
     */
    fun removeMultiEdges(): CpgGraph = combineCoDirectedEdges().combineAntiDirectedEdges()

    private fun combineCoDirectedEdges() = edges.groupBy { it.source to it.target }
        .map { (coordinates, parallelEdges) ->
            val newLabel = parallelEdges.joinToString(", ") { it.attributes.label ?: "" }
            val newId = parallelEdges.joinToString(", ") { it.key }
            val newSize = parallelEdges.first().attributes.size
            val paintColor = if (parallelEdges.size > 1) "#FFD5D4" else null
            CpgEdge(
                newId,
                coordinates.first,
                coordinates.second,
                CpgEdgeAttributes(newLabel, paintColor, newSize)
            )
        }
        .let { newEdges ->
            copy(edges = newEdges)
        }

    private fun combineAntiDirectedEdges() = edges.groupBy {
        maxOf(it.source, it.target) to minOf(it.source, it.target)
    }
        .map { (coordinates, parallelEdges) ->
            require(parallelEdges.size <= 2)
            val newLabel = parallelEdges.joinToString(" | ") { it.attributes.label ?: "" }
            val newId = parallelEdges.joinToString(" | ") { it.key }
            val newSize = parallelEdges.first().attributes.size
            val paintColor = if (parallelEdges.size != 1) {
                "#FA90FA"
            } else {
                parallelEdges[0].attributes.color ?: if (parallelEdges.size == 2) {
                    parallelEdges[1].attributes.color
                } else {
                    null
                }
            }
            listOf(
                CpgEdge(
                    newId,
                    coordinates.first,
                    coordinates.second,
                    CpgEdgeAttributes(newLabel, paintColor, newSize)
                ),
                CpgEdge(
                    "$newId-reversed",
                    coordinates.second,
                    coordinates.first,
                    CpgEdgeAttributes("", paintColor, newSize)
                )
            )
        }
        .flatten()
        .let { newEdges ->
            copy(edges = newEdges)
        }

    companion object {
        /**
         * Placeholder of a graph
         */
        val placeholder = CpgGraph(
            nodes = emptyList(),
            edges = emptyList(),
            options = CpgGraphOptions(),
            attributes = CpgGraphAttributes()
        )

        /**
         * Generate random graph with [numberOfNodes] nodes and [numberOfEdges] edges
         *
         * @param numberOfNodes requested amount of nodes in generated graph
         * @param numberOfEdges requested amount of edges in generated graph
         * @return generated graph with [numberOfNodes] nodes and [numberOfEdges] edges
         */
        fun randomGraph(numberOfNodes: Long, numberOfEdges: Long): CpgGraph = CpgGraph(
            LongRange(0, numberOfNodes - 1).map {
                CpgNode(
                    "$it-node",
                    CpgNodeAttributes(label = "$it-node"),
                )
            },
            LongRange(0, numberOfEdges - 1).map {
                CpgEdge(
                    "$it-edge",
                    "${Random.nextLong(0, numberOfNodes - 1)}-node",
                    "${Random.nextLong(0, numberOfNodes - 1)}-node",
                    CpgEdgeAttributes(label = "$it-edge"),
                )
            },
        )
    }
}

/**
 * @property name seems to be an id of a graph
 */
@Serializable
@ExperimentalSerializationApi
data class CpgGraphAttributes(
    @EncodeDefault(ALWAYS) val name: String = "Demo graph",
)

/**
 * @property type
 * @property multi
 * @property allowSelfLoops
 */
@Serializable
@ExperimentalSerializationApi
data class CpgGraphOptions(
    @EncodeDefault(ALWAYS) val type: String = "directed",
    @EncodeDefault(ALWAYS) val multi: Boolean = true,
    @EncodeDefault(ALWAYS) val allowSelfLoops: Boolean = true,
)




© 2015 - 2025 Weber Informatics LLC | Privacy Policy