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

ai.platon.pulsar.persist.graph.WebGraph.kt Maven / Gradle / Ivy

package ai.platon.pulsar.persist.graph

import ai.platon.pulsar.common.config.VolatileConfig
import ai.platon.pulsar.persist.WebPage
import org.jgrapht.ext.*
import org.jgrapht.graph.DirectedWeightedPseudograph
import java.io.StringWriter
import java.io.Writer
import java.util.*

/**
 * Created by vincent on 16-12-21.
 * Copyright @ 2013-2016 Platon AI. All rights reserved
 *
 * A pseudograph is a non-simple graph in which both graph loops and multiple edges are permitted.
 */
class WebGraph : DirectedWeightedPseudograph {
    var focus: WebVertex? = null
        set(focus) {
            if (!containsVertex(focus)) {
                addVertex(focus)
            }
            field = focus
        }

    constructor() : super(WebEdge::class.java)

    constructor(sourceVertex: WebVertex, targetVertex: WebVertex) : super(WebEdge::class.java) {
        addEdgeLenient(sourceVertex, targetVertex, 0.0)
    }

    fun addOrUpdateWebVertex(vertex: WebVertex): WebVertex {
        val added = addVertex(vertex)

        if (!added) {
            val v = find(vertex)
            if (v != null) {
                v.page = vertex.page
                return v
            } else {
                throw IllegalStateException("Vertex must be in the graph | ${vertex.url}")
            }
        }

        return vertex
    }

    @JvmOverloads
    fun addEdgeLenient(sourceVertex: WebVertex, targetVertex: WebVertex, weight: Double = 0.0): WebEdge {
        val v1 = addOrUpdateWebVertex(sourceVertex)
        val v2 = addOrUpdateWebVertex(targetVertex)
        val edge = addEdge(v1, v2)!!
        setEdgeWeight(edge, weight)
        return edge
    }

    fun addEdge(otherEdge: WebEdge, otherGraph: WebGraph): WebEdge {
        val sourceVertex = otherEdge.source
        val targetVertex = otherEdge.target
        val weight = otherGraph.getEdgeWeight(otherEdge)
        val metadata = otherEdge.metadata
        val edge = addEdgeLenient(sourceVertex, targetVertex, weight)
        edge.metadata = metadata
        return edge
    }

    fun combine(otherGraph: WebGraph): WebGraph {
        otherGraph.vertexSet().forEach { addOrUpdateWebVertex(it) }
        otherGraph.edgeSet().forEach { addEdge(it, otherGraph) }
        return this
    }

    fun find(vertex: WebVertex): WebVertex? {
        return vertexSet().firstOrNull { it == vertex }
    }

    fun find(url: String): WebVertex? {
        return vertexSet().firstOrNull { it.url == url }
    }

    fun firstEdge(): WebEdge? {
        val it = edgeSet().iterator()
        return it.takeIf { it.hasNext() }?.next()
    }

    override fun toString(): String {
        val exporter = createExporter()
        val writer: Writer = StringWriter()
        try {
            exporter.exportGraph(this, writer)
        } catch (e: ExportException) {
            return e.toString()
        }
        return writer.toString()
    }

    companion object {
        val EMPTY = WebGraph()

        fun of(edge: WebEdge, graph: WebGraph): WebGraph {
            val subgraph = WebGraph()
            subgraph.addEdge(edge, graph)
            return subgraph
        }

        /**
         * Create exporter
         */
        fun createExporter(): GraphExporter {
            // create GraphML exporter
            val exporter = GraphMLExporter(
                { it.url },
                    null,
                { it.toString() },
                    null
            )
            // set to export the internal edge weights
            exporter.isExportEdgeWeights = true
            // register additional name attribute for vertices and edges
            exporter.registerAttribute("url", GraphMLExporter.AttributeCategory.ALL, GraphMLExporter.AttributeType.STRING)
            // register additional color attribute for vertices
            exporter.registerAttribute("depth", GraphMLExporter.AttributeCategory.NODE, GraphMLExporter.AttributeType.INT)
            // create provider of vertex attributes
            val vertexAttributeProvider = ComponentAttributeProvider { v: WebVertex ->
                val m: MutableMap = HashMap()
                m["baseUrl"] = v.page!!.location
                m["depth"] = v.page!!.distance.toString()
                m
            }
            exporter.vertexAttributeProvider = vertexAttributeProvider
            // create provider of edge attributes
            val edgeAttributeProvider = ComponentAttributeProvider { e: WebEdge ->
                val m: MutableMap = HashMap()
                m["name"] = e.toString()
                m
            }
            exporter.edgeAttributeProvider = edgeAttributeProvider
            return exporter
        }

        /**
         * Create importer
         */
        fun createImporter(): GraphImporter { // create vertex provider
            val vertexProvider = VertexProvider { url: String, attributes: Map ->
                val baseUrl = attributes["baseUrl"]
                val depth = Integer.valueOf(attributes["depth"])
                val page = WebPage.newWebPage(url, VolatileConfig.UNSAFE)
                page.location = baseUrl
                page.distance = depth
                WebVertex(url, WebPage.newWebPage(url, VolatileConfig.UNSAFE))
            }
            // create edge provider
            val edgeProvider = EdgeProvider {
                from: WebVertex, to: WebVertex, label: String, attributes: Map -> WebEdge()
            }

            // create GraphML importer
            return GraphMLImporter(vertexProvider, edgeProvider)
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy